72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
"""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):
|
|
lines = open(path, encoding='utf8', errors='ignore').read().split('\n')
|
|
for i, l in enumerate(lines):
|
|
if 'soothe2_x64.vst3' in l:
|
|
break
|
|
chunks = []
|
|
for l in lines[i + 1:]:
|
|
s = l.strip()
|
|
if s == '}':
|
|
break
|
|
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 {}
|
|
|
|
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]}') |