74 lines
2.3 KiB
Python
74 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Decodes Argonaut ASF music (v2.1, 4-bit ADPCM) like the game's asfcodec.adl.
|
|
|
|
Reimplemented from the disassembly of asfcodec.adl (routines 0x26e01969 / 0x26e01ca1):
|
|
Filter 0: s = s1 + (n << shift)
|
|
Filter 1: s = 2*s1 - s2 + (n << shift) (+ carry from 'adc', see below)
|
|
- no clipping; output is stored as 16 bit (overflow = wrap)
|
|
- within a block s1 carries on untruncated in the register,
|
|
at the start of a block it is read from the 16-bit buffer
|
|
- s2 is always read from the 16-bit buffer
|
|
Usage: asf2wav.py input.asf output.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)] # stored 16-bit values: [second to last, last]
|
|
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 # block start: s1 from memory
|
|
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())
|