#!/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('> 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())