74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Dekodiert Argonaut-ASF-Musik (v2.1, 4-Bit-ADPCM) wie asfcodec.adl aus dem Spiel.
|
|
|
|
Nachgebaut aus der Disassembly von asfcodec.adl (Routinen 0x26e01969 / 0x26e01ca1):
|
|
Filter 0: s = s1 + (n << shift)
|
|
Filter 1: s = 2*s1 - s2 + (n << shift) (+ Übertrag aus 'adc', s. u.)
|
|
- kein Clipping; Ausgabe wird als 16 Bit gespeichert (Überlauf = Wrap)
|
|
- s1 läuft innerhalb eines Blocks ungekürzt im Register weiter,
|
|
am Blockanfang wird es aus dem 16-Bit-Puffer gelesen
|
|
- s2 wird immer aus dem 16-Bit-Puffer gelesen
|
|
Aufruf: asf2wav.py eingabe.asf ausgabe.wav
|
|
"""
|
|
import array
|
|
import struct
|
|
import sys
|
|
import wave
|
|
|
|
M32 = 0xFFFFFFFF
|
|
|
|
|
|
def s32(x):
|
|
x &= M32
|
|
return x - (1 << 32) if x & 0x80000000 else x
|
|
|
|
|
|
def wrap16(x):
|
|
return ((x + 0x8000) & 0xFFFF) - 0x8000
|
|
|
|
|
|
def decode(data):
|
|
off = struct.unpack_from('<I', data, 12)[0]
|
|
nblocks, _, _, rate, _, flags = struct.unpack_from('<IIIHHI', data, off)
|
|
ch = 2 if flags & 2 else 1
|
|
p = off + 20
|
|
out = array.array('h', bytes(nblocks * 32 * ch * 2))
|
|
mem = [[0, 0] for _ in range(ch)] # gespeicherte 16-Bit-Werte: [vorletztes, letztes]
|
|
o = 0
|
|
for _ in range(nblocks):
|
|
for c in range(ch):
|
|
ctl = data[p]
|
|
blk = data[p + 1:p + 17]
|
|
p += 17
|
|
cl = (ctl >> 4) + 2
|
|
prev2, prev1 = mem[c]
|
|
eax = prev1 # Blockanfang: s1 aus dem Speicher
|
|
k = o + c
|
|
for byte in blk:
|
|
for nib in (byte >> 4, byte & 15):
|
|
n = nib - 16 if nib & 8 else nib
|
|
edx = s32((n * 16) << cl)
|
|
if ctl & 4:
|
|
ebx = s32(eax << 7)
|
|
a = s32(-prev2 << 6)
|
|
carry = ((a & M32) + (edx & M32)) >> 32
|
|
eax = s32(a + edx + ebx + carry) >> 6
|
|
else:
|
|
eax = s32((eax << 6) + edx) >> 6
|
|
w = wrap16(eax)
|
|
out[k] = w
|
|
k += ch
|
|
prev2, prev1 = prev1, w
|
|
mem[c] = [prev2, prev1]
|
|
o += 32 * ch
|
|
return rate, ch, out
|
|
|
|
|
|
if __name__ == '__main__':
|
|
rate, ch, pcm = decode(open(sys.argv[1], 'rb').read())
|
|
with wave.open(sys.argv[2], 'wb') as w:
|
|
w.setnchannels(ch)
|
|
w.setsampwidth(2)
|
|
w.setframerate(rate)
|
|
w.writeframes(pcm.tobytes())
|