#!/usr/bin/env python3 """sf2extract.py -- extract the BattleTech AWE32 SoundFonts (AUDIO1/2.RES) into loose WAV files + generate the allPresets[] C++ table for the WinTesla port. The banks are SoundFont **v1.0** (ifil 1.0, EMU8000): the `shdr` sample headers are 16 bytes {dwStart,dwEnd,dwStartLoop,dwEndLoop} (NO name/rate field -- the v2.0 46-byte layout does not apply). Samples are 16-bit mono PCM in `smpl`. Preset -> sample resolves through phdr->pbag->pgen(inst=41)->inst->ibag->igen(sampleID=53)->shdr. Output: content/AUDIO/.wav (one per preset; the L4AUDRES loader sf_opens these) game/reconstructed/audiopresets.cpp (the allPresets[2][100] table, replaces the btstubs.cpp zero-init stub) Sample rate: SF2 v1.0 shdr has no per-sample rate; these were authored at 22050 Hz (SAMPLE_RATE below -- adjust if pitch is wrong). Loop is taken from the shdr loop points (a real loop region -> LoopAtWill; else ForceStatic one-shot). """ import struct, os, wave HERE = os.path.dirname(__file__) AUDIO = os.path.join(HERE, "..", "content", "AUDIO") OUT_CPP = os.path.join(HERE, "..", "game", "reconstructed", "audiopresets.cpp") SAMPLE_RATE = 22050 def parse_bank(path): d = open(path, "rb").read() assert d[:4]==b"RIFF" and d[8:12]==b"sfbk" def find(fc): p=d.find(fc); return p+8, struct.unpack_from(" sample map recovered from the AWE32 SoundFonts\n') f.write('// (AUDIO1/2.RES, SF2 v1.0). Each preset -> a loose .wav the\n') f.write('// L4AUDRES loader sf_opens. Regenerate: python scratchpad/sf2extract.py\n') f.write('#include \n\n') f.write('PRESETINFO allPresets[2][128] = {};\n\n') f.write('namespace {\nstruct AudioPresetInit {\n\tAudioPresetInit() {\n') for bn in (1,2): for preset, (name, looping) in sorted(table[bn].items()): loop = "SampleLoop::LoopAtWill" if looping else "SampleLoop::ForceStatic" f.write(f'\t\t{{ SAMPLEINFO &s = allPresets[{bn-1}][{preset}].samples[0];\n') f.write(f'\t\t allPresets[{bn-1}][{preset}].sampleNum = 1;\n') f.write(f'\t\t s.implemented = true; s.bufferIndex = -1; s.file = "{name}.wav";\n') f.write(f'\t\t s.chan = SampleChannel::CHANNEL_CENTER; s.loop = {loop}; }}\n') f.write('\t}\n} s_audioPresetInit;\n}\n') total = len(table[1]) + len(table[2]) print(f"wrote {OUT_CPP} ({total} presets)") if __name__ == "__main__": main()