#!/usr/bin/env python3 """rtctx_rt.py — P1.5: find live ctx via REAPER realtime transport playback. Launches REAPER with render_long.rpp + play.lua (realtime audio playback, host audio callback alive), then snapshots the yabridge-host memory during playback and searches for the DSP ctx by FUN_180535ae0 constructor markers. Usage: rtctx_rt.py """ import subprocess, os, glob, time, struct, sys, json SNAP = '/tmp/snap_rt.bin' def mem_open(pid): return os.open(f'/proc/{pid}/mem', os.O_RDONLY) def find_host(): for p in glob.glob('/proc/[0-9]*'): pid = int(os.path.basename(p)) try: cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace') maps = open(f'/proc/{pid}/maps').read() except Exception: continue if 'soothe2' in maps and 'reaper' not in cmd: return pid return None def snapshot(host): fd = mem_open(host) out = open(SNAP, 'wb') nreg = 0; nbytes = 0; t0 = time.time() for line in open(f'/proc/{host}/maps').read().splitlines(): p = line.split() if len(p) < 2: continue lo, hi = (int(x, 16) for x in p[0].split('-')) if 'r' not in p[1]: continue a = lo while a < hi: n = min(hi - a, 8 * 1024 * 1024) try: d = os.pread(fd, n, a) except Exception: a += n; continue if not d: a += n; continue out.write(struct.pack(' len(body): good = False; break c = struct.unpack_from(' 0x40: dedup.append(h) return dedup def dump_targets(regs, ctx): out = {} def f32(addr): b = readabs(regs, addr, 4) return struct.unpack('= 8 * n else None out['scalars'] = { '0x24': f32(ctx + 0x24), '0x540870': f32(ctx + 0x540870), '0x540874': f32(ctx + 0x540874), '0x54087c': f32(ctx + 0x54087c), '0x540888': f32(ctx + 0x540888), '0x54088c': f32(ctx + 0x54088c), } out['bandconfig0'] = { 'A': f32(ctx + 0x188), 'B': f32(ctx + 0x188 + 0x04), 'gamma': f32(ctx + 0x188 + 0x0c), 'flag': readabs(regs, ctx + 0x188 + 0x10, 1), } for name, off, cnt in [('0x4c0528', 0x4c0528, 32), ('0x3c0510', 0x3c0510, 32), ('0x2c04f8', 0x2c04f8, 32)]: a = f64arr(ctx + off, cnt) if a is not None: out[name] = a return out def main(): os.system("pkill -9 -x reaper 2>/dev/null; pkill -9 -f '[y]abridge' 2>/dev/null; sleep 1") proc = subprocess.Popen( ['/usr/bin/reaper', '-nosplash', '-ignoreerrors', '/home/m/soothe-bt/render_long.rpp', '/home/m/re-tools/play.lua'], stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) t0 = time.time(); host = None while time.time() - t0 < 60 and not host: host = find_host(); time.sleep(0.2) if not host: print('NO HOST'); return 1 print('host', host, 'at %.1fs' % (time.time() - t0)) time.sleep(8) for attempt in range(3): nreg, nbytes = snapshot(host) print('snapshot #%d: %d regs %.1f MB' % (attempt, nreg, nbytes / 1e6)) if nbytes > 50e6: break time.sleep(3) if proc.poll() is None: proc.kill() regs = parse_snap() hits = find_registry(regs) rb = None for h in hits: b = readabs(regs, h, 8) if b and struct.unpack('