From 9bcc64872bfa042349e959f4baf079d21707cf56 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 25 Mar 2025 14:19:18 +0100 Subject: vorbis setup header reconstruction is hard --- Cargo.lock | 36 +++++++++++++++ Cargo.toml | 1 + src/classes/audio_clip.rs | 3 +- src/fmod.rs | 114 ++++++++++++++++++++++++++++++++++++---------- 4 files changed, 128 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67645ec..a5b1bca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -600,6 +600,17 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder 1.5.0", + "ogg", + "tinyvec", +] + [[package]] name = "libc" version = "0.2.171" @@ -769,6 +780,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "ogg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +dependencies = [ + "byteorder 1.5.0", +] + [[package]] name = "once_cell" version = "1.20.3" @@ -1190,6 +1210,21 @@ dependencies = [ "weezl", ] +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml" version = "0.8.20" @@ -1249,6 +1284,7 @@ dependencies = [ "glam", "humansize", "image", + "lewton", "log", "lz4_flex", "lzma", diff --git a/Cargo.toml b/Cargo.toml index 702632a..ebde4a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ glam = { version = "0.30.0", features = ["serde"] } texpresso = "2.0.1" image = "0.25.5" texture2ddecoder = { git = "https://github.com/UniversalGameExtraction/texture2ddecoder", rev = "d2b4653fda298f1da39917da86bc509b17879808" } +lewton = "0.10.2" diff --git a/src/classes/audio_clip.rs b/src/classes/audio_clip.rs index a3e8d22..8f027ad 100644 --- a/src/classes/audio_clip.rs +++ b/src/classes/audio_clip.rs @@ -52,7 +52,8 @@ impl AudioClip { let mut bank = FmodSoundBank::open(Cursor::new(data))?; // Ok(buf.into_inner()) for i in 0..bank.streams.len() { - let data = bank.read_stream(i)?; + let data = bank.read_stream_raw(i)?; + // eprintln!("{data:?}") } Ok(Vec::new()) diff --git a/src/fmod.rs b/src/fmod.rs index f0e7b4c..8713a56 100644 --- a/src/fmod.rs +++ b/src/fmod.rs @@ -1,7 +1,11 @@ use crate::helper::ReadExt; -use anyhow::{Ok, Result, bail}; +use anyhow::{Result, bail}; +use lewton::{ + audio::PreviousWindowRight, + header::{IdentHeader, SetupHeader, read_header_ident}, +}; use log::debug; -use std::io::{Cursor, Read, Seek, SeekFrom}; +use std::io::{Cursor, Read, Seek, SeekFrom, Write}; pub struct FmodSoundBank { inner: T, @@ -12,6 +16,7 @@ pub struct FmodSoundBank { pub struct StreamInfo { header: StreamHeader, + channels: usize, } struct FsbHeader { @@ -95,18 +100,22 @@ impl FmodSoundBank { for _ in 0..header.num_streams { let header = StreamHeader::from_packed(file.read_u64_le()?)?; - eprintln!("{header:?}"); 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(StreamInfo { header }); + streams.push(info); } if header.name_table_size != 0 { @@ -132,22 +141,33 @@ impl FmodSoundBank { inner: file, }) } - pub fn read_stream(&mut self, n: usize) -> Result<()> { + fn stream_read_range(&self, n: usize) -> Result<(u64, u64)> { let info = &self.streams[n]; - - let start = info.header.data_offset; + 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); + .map_or(self.header.data_size, |s| s.header.data_offset) as u64; - let mut data = vec![0; (end - start) as usize]; - debug!("{} {}", start, end); - self.inner - .seek(SeekFrom::Start(start as u64 + self.data_base_offset))?; + 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(()) + 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()) } } @@ -161,16 +181,17 @@ struct StreamHeader { } impl StreamHeader { const MASKS: &[u64] = &[ - 0b1000000000000000000000000000000000000000000000000000000000000000, - 0b0111100000000000000000000000000000000000000000000000000000000000, - 0b0000011000000000000000000000000000000000000000000000000000000000, - 0b0000000111111111111111111111111111000000000000000000000000000000, - 0b0000000000000000000000000000000000111111111111111111111111111111, + 0b0000000000000000000000000000000000000000000000000000000000000001, + 0b0000000000000000000000000000000000000000000000000000000000011110, + 0b0000000000000000000000000000000000000000000000000000000001100000, + 0b0000000000000000000000000000001111111111111111111111111110000000, + 0b1111111111111111111111111111110000000000000000000000000000000000, ]; pub fn from_packed(x: u64) -> Result { - let next_chunk = x & Self::MASKS[0] != 0; + 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 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 { @@ -202,6 +223,7 @@ impl StreamHeader { } } +#[derive(Debug)] struct ChunkHeader { next_chunk: bool, size: u32, @@ -209,9 +231,9 @@ struct ChunkHeader { } impl ChunkHeader { const MASKS: &[u32] = &[ - 0b10000000000000000000000000000000, - 0b01111111111111111111111110000000, - 0b00000000000000000000000001111111, + 0b00000000000000000000000000000001, + 0b00000001111111111111111111111110, + 0b11111110000000000000000000000000, ]; pub fn from_packed(x: u32) -> Result { let next_chunk = x & Self::MASKS[0] != 0; @@ -250,3 +272,45 @@ impl ChunkKind { } } } + +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!() +} -- cgit v1.2.3-70-g09d2