P0: fix build (levelpath in CMake, FLOOR_LIN, exact LUT curve formulas), robust RPP param decoder (607 rpp ok), preserve f_52b570/f529fe0 disasms

This commit is contained in:
2026-08-19 21:54:26 +03:00
parent 55623b998f
commit 58164f2952
3 changed files with 99 additions and 88 deletions
+63 -32
View File
@@ -1,41 +1,72 @@
#!/usr/bin/env python3
import base64, re, sys, html
"""Extract soothe2 plugin params from an .rpp as a JSON dict.
Reaper stores the VST3 state as base64: line1 after <VST> is a short binary
header, following lines (128-char chunks) are the outer b64 blob which wraps
<SOOTHE2STATE><PARAM .../>...</SOOTHE2STATE> at byte offset 16.
"""
import base64, re, html, sys
def b64dec(s):
s = re.sub(r'[^A-Za-z0-9+/=]', '', s)
best = b''
for pad in range(4):
s2 = s + '=' * pad
try:
d = base64.b64decode(s2, validate=False)
if len(d) > len(best):
best = d
except Exception:
continue
# if nothing cleanly decodes, brute-force by trimming tail bad chars
if not best:
for cut in range(1, min(8, len(s))):
try:
d = base64.b64decode(s[:-cut], validate=False)
if len(d) > len(best):
best = d
except Exception:
continue
return best
def rpp_params(path):
txt = open(path, encoding='utf8', errors='ignore').read()
lines = txt.split('\n')
lines = open(path, encoding='utf8', errors='ignore').read().split('\n')
for i, l in enumerate(lines):
if 'soothe2_x64.vst3' in l:
break
b = []
chunks = []
for l in lines[i + 1:]:
if l.strip() == '}':
s = l.strip()
if s == '}':
break
b.append(l.strip())
raw = re.sub(r'[^A-Za-z0-9+/=]', '', ''.join(b))
raw = raw.rstrip('=')
# drop chars until b64 len divisible by 4 (the leading binary header causes odd)
while (len(raw) % 4) != 0:
raw = raw[:-1]
padded = raw + '=' * ((-len(raw)) % 4)
dec = base64.b64decode(padded, validate=False)
p = dec.find(b'<?xml')
xml = dec[p:].decode('utf8', 'ignore')
xml = html.unescape(xml)
params = dict(re.findall(r'<PARAM id="([^"]+)" value="([^"]+)"', xml))
# also decode processorStateData inner
proc = re.search(r'processorStateData="([^"]+)"', xml)
if proc:
inner = proc.group(1)
try:
inner_dec = base64.b64decode(re.sub(r'\s', '', inner))
print(' [processorStateData inner]', inner_dec.decode('utf8', 'ignore')[:400])
except Exception as e:
print(' proc inner err', e)
return params
if s:
chunks.append(s)
# Some RPPs wrap the param blob with the short binary-header line as the
# first entry, others start directly with the payload line. Try both joins.
for text in (''.join(chunks), ''.join(chunks[1:])):
outer = b64dec(text)
p = outer.find(b'<?xml')
if p < 0:
continue
xml = html.unescape(outer[p:].decode('utf8', 'ignore'))
params = dict(re.findall(r'<PARAM id="([^"]+)" value="([^"]+)"', xml))
proc = re.search(r'processorStateData="([^"]+)"', xml)
if proc:
inner = b64dec(re.sub(r'\s', '', proc.group(1)))
params['processorStateData'] = inner.decode('utf8', 'ignore')
if params:
return params
return {}
for f in sys.argv[1:]:
p = rpp_params(f)
print('===', f.split('/')[-1])
for k in sorted(p):
print(f' {k:24s} = {p[k]}')
def params_json(path):
p = rpp_params(path)
p.pop('processorStateData', None)
return p
if __name__ == '__main__':
import json
for f in sys.argv[1:]:
p = params_json(f)
print('===', f.split('/')[-1])
for k in sorted(p):
print(f' {k:22s} = {p[k]}')