use crate::helper::ReadExt; use anyhow::{Result, bail}; use lewton::{ audio::PreviousWindowRight, header::{IdentHeader, SetupHeader, read_header_ident}, }; use log::debug; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; pub struct FmodSoundBank { inner: T, data_base_offset: u64, header: FsbHeader, pub streams: Vec, } pub struct StreamInfo { header: StreamHeader, channels: usize, } struct FsbHeader { version: u32, num_streams: u32, stream_headers_size: u32, name_table_size: u32, data_size: u32, format: FsbFormat, } #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum FsbFormat { None, Pcm8, Pcm16, Pcm24, Pcm32, PcmFloat, GcAdPcm, ImaAdPcm, Vag, HeVag, Xma, Mpeg, Celt, At9, Xwma, Vorbis, FAdPcm, Opus, } impl FsbHeader { pub fn read(mut f: impl Read) -> Result { let mut buf = [0; 4]; f.read_exact(&mut buf)?; if &buf != b"FSB5" { bail!("invalid magic") } let version = f.read_u32_le()?; let num_streams = f.read_u32_le()?; let stream_headers_size = f.read_u32_le()?; let name_table_size = f.read_u32_le()?; let data_size = f.read_u32_le()?; let format = FsbFormat::from_num(f.read_u32_le()?)?; debug!("FSB version: {version}"); debug!("Sample Count: {num_streams}"); debug!("Format: {format:?}"); f.discard(32)?; Ok(Self { data_size, format, name_table_size, num_streams, stream_headers_size, version, }) } } impl FsbFormat { pub fn from_num(x: u32) -> Result { if x < 18 { Ok(unsafe { std::mem::transmute(x) }) } else { bail!("audio format out of range") } } } impl FmodSoundBank { pub fn open(mut file: T) -> Result { let header = FsbHeader::read(&mut file)?; let mut streams = Vec::new(); for _ in 0..header.num_streams { let header = StreamHeader::from_packed(file.read_u64_le()?)?; let mut next = header.next_chunk; let mut info = StreamInfo { channels: header.channels as usize, header, }; while next { let chunk = ChunkHeader::from_packed(file.read_u32_le()?)?; match chunk.kind { ChunkKind::Channels => { info.channels = file.read_u64_le()? as usize; } k => todo!("chunk kind {k:?}"), } next = chunk.next_chunk; } streams.push(info); } if header.name_table_size != 0 { let mut offsets = Vec::new(); for _ in 0..header.num_streams { offsets.push(file.read_u32_le()?); } let mut buf = vec![0u8; header.name_table_size as usize]; file.read_exact(&mut buf)?; let mut buf = Cursor::new(buf); let mut names = Vec::new(); for o in offsets { buf.seek(SeekFrom::Start(o as u64))?; names.push(buf.read_cstr()?); } } let data_base_offset = file.stream_position()?; Ok(Self { data_base_offset, header, streams, inner: file, }) } fn stream_read_range(&self, n: usize) -> Result<(u64, u64)> { let info = &self.streams[n]; let start = info.header.data_offset as u64; let end = self .streams .get(n + 1) .map_or(self.header.data_size, |s| s.header.data_offset) as u64; Ok((start + self.data_base_offset, end - start)) } pub fn read_stream_raw(&mut self, n: usize) -> Result> { let (offset, size) = self.stream_read_range(n)?; let mut data = vec![0; size as usize]; self.inner.seek(SeekFrom::Start(offset))?; self.inner.read_exact(&mut data)?; Ok(data) } pub fn read_stream(&mut self, n: usize) -> Result> { let (offset, size) = self.stream_read_range(n)?; match self.header.format { FsbFormat::Vorbis => {} _ => todo!(), } Ok(Vec::new()) } } #[derive(Debug)] struct StreamHeader { next_chunk: bool, samplerate: u32, channels: u8, data_offset: u32, samples: u32, } impl StreamHeader { const MASKS: &[u64] = &[ 0b0000000000000000000000000000000000000000000000000000000000000001, 0b0000000000000000000000000000000000000000000000000000000000011110, 0b0000000000000000000000000000000000000000000000000000000001100000, 0b0000000000000000000000000000001111111111111111111111111110000000, 0b1111111111111111111111111111110000000000000000000000000000000000, ]; pub fn from_packed(x: u64) -> Result { eprintln!("{x:032b}"); let next_chunk = (x & Self::MASKS[0]) != 0; let samplerate_raw = (x & Self::MASKS[1]) >> Self::MASKS[1].trailing_zeros(); let channels_raw = (x & Self::MASKS[2]) >> Self::MASKS[2].trailing_zeros(); let data_offset = (x & Self::MASKS[3]) >> Self::MASKS[3].trailing_zeros(); let samples = x & Self::MASKS[4]; Ok(Self { next_chunk, samplerate: match samplerate_raw { 0 => 4000, 1 => 8000, 2 => 11000, 3 => 11025, 4 => 16000, 5 => 22050, 6 => 24000, 7 => 32000, 8 => 44100, 9 => 48000, 10 => 96000, x => bail!("invalid sampling frequency: {x}"), }, channels: match channels_raw { 0 => 1, 1 => 2, 2 => 4, 3 => 8, x => bail!("invalid channel count: {x}"), }, data_offset: data_offset as u32 * 16, samples: samples as u32, }) } } #[derive(Debug)] struct ChunkHeader { next_chunk: bool, size: u32, kind: ChunkKind, } impl ChunkHeader { const MASKS: &[u32] = &[ 0b00000000000000000000000000000001, 0b00000001111111111111111111111110, 0b11111110000000000000000000000000, ]; pub fn from_packed(x: u32) -> Result { let next_chunk = x & Self::MASKS[0] != 0; let size = (x & Self::MASKS[1]) >> Self::MASKS[1].trailing_zeros(); let kind = x & Self::MASKS[2]; Ok(Self { kind: ChunkKind::from_num(kind as u8)?, size, next_chunk, }) } } #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq)] enum ChunkKind { Channels, Samplerate, Loop, Comment, XmaSeekTable, DspCoefficients, Atrac9Config, XwmaConfig, VorbisSeekTable, PeakVolume, VorbisIntraLayers, OpusDataSize, } impl ChunkKind { pub fn from_num(n: u8) -> Result { if n < 13 { Ok(unsafe { std::mem::transmute(n) }) } else { bail!("unknown chunk type {n:02x}") } } } pub struct VorbisStreamReader { inner: T, ident: IdentHeader, setup: SetupHeader, previous_window: PreviousWindowRight, } impl VorbisStreamReader { fn read_packet(&mut self) -> Result> { let packet_size = self.inner.read_u16_le()?; let mut buf = vec![0; packet_size as usize]; self.inner.read_exact(&mut buf)?; Ok(buf) } fn decode_packet(&mut self) -> Result>> { let packet = self.read_packet()?; Ok(lewton::audio::read_audio_packet_generic( &self.ident, &self.setup, &packet, &mut self.previous_window, )?) } } fn make_vorbis_ident(channels: u8, sample_rate: u32) -> Result { let mut packet = Vec::new(); packet.write_all(&[1])?; packet.write_all(b"vorbis")?; packet.write_all(&0u32.to_le_bytes())?; packet.write_all(&[channels])?; packet.write_all(&sample_rate.to_le_bytes())?; packet.write_all(&0u32.to_le_bytes())?; packet.write_all(&0u32.to_le_bytes())?; packet.write_all(&0u32.to_le_bytes())?; packet.write_all(&[(8 << 4) | 11])?; packet.write_all(&1u8.to_le_bytes())?; Ok(read_header_ident(&packet)?) } fn make_vorbis_setup() -> Result { todo!() }