//! Marathon Sounds format handling. use crate::durandal::{bin::*, err::*, fixed::*, sound::*}; use bitflags::bitflags; use std::collections::BTreeMap; /// Reads a sound. pub fn read_sound(b: &[u8]) -> ResultS { read_data! { 21, BE in b => len = u32[4] as usize; rate = u16[8]; lp_beg = u32[12] as usize; lp_end = u32[16] as usize; magic = u8[20]; } match magic { 0 => { let stream = &b[22..22 + len]; Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream)) } 0xFF => { read_data! { 63, BE in b => len = u32[22] as usize; bps = u16[48]; } match bps { 16 => { let stream = &b[63..63 + len * 2]; Ok(Sound16::new_from_16(rate, lp_beg, lp_end, stream)) } 8 => { let stream = &b[63..63 + len]; Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream)) } _ => bail!("bad bits per sample"), } } _ => bail!("invalid magic number"), } } /// Reads a sound definition. pub fn read_sound_def(b: &[u8]) -> ResultS, u16, SoundDef)>> { read_data! { 64, BE in b => index = u16[0]; volume = u16[2]; flags = u16[4]; chance = u16[6]; pitch_lo = Fixed[8]; pitch_hi = Fixed[12]; n_sounds = u16[16] as usize; grp_ofs = u32[20] as usize; } let flags = flag_ok!(SoundFlags, flags)?; let volume = Volume::from_repr(volume)?; if index == u16::max_value() { return Ok(None); } if n_sounds > 5 { bail!("too many sounds"); } let mut ofs = Vec::with_capacity(n_sounds); let mut p = 36; for _ in 0..n_sounds { ofs.push(grp_ofs + u32b(&b[p..]) as usize); p += 4; } let header = SoundHead{volume, flags, chance, pitch_lo, pitch_hi}; let sounds = Vec::with_capacity(n_sounds); Ok(Some((ofs, index, SoundDef{header, sounds}))) } /// Reads all sounds from a Sound file. pub fn read_sounds(b: &[u8]) -> ResultS> { read_data! { 260, BE in b => version = u32[0]; magic = Ident[4]; src_num = u16[8] as usize; snd_num = u16[10] as usize; } if version != 1 || magic != *b"snd2" { bail!("bad sound header"); } let mut sc = Vec::with_capacity(src_num); let mut p = 260; for _ in 0..src_num { let mut st = BTreeMap::new(); for _ in 0..snd_num { if let Some((ofs, idx, mut def)) = read_sound_def(&b[p..p + 64])? { for &ofs in &ofs { def.sounds.push(read_sound(&b[ofs..])?); } st.insert(idx, def); } p += 64; } sc.push(st); } Ok(sc) } /// The header of a sound definition. #[derive(Debug, serde::Serialize)] pub struct SoundHead { pub volume: Volume, pub flags: SoundFlags, pub chance: u16, pub pitch_lo: Fixed, pub pitch_hi: Fixed, } /// A sound definition containing one, many or no sounds. pub struct SoundDef { pub header: SoundHead, pub sounds: Vec, } /// A table of sound definitions. pub type SoundTable = BTreeMap; bitflags! { /// Flags for `SoundHead`. #[derive(serde::Serialize)] pub struct SoundFlags: u16 { const NoRestart = 1; const NoChannelSwitch = 1 << 1; const LessPitchChange = 1 << 2; const NoPitchChange = 1 << 3; const NoObstruction = 1 << 4; const NoMediaObstruct = 1 << 5; const Ambient = 1 << 6; } } c_enum! { /// The type of volume this sound has. #[derive(Debug, serde::Serialize)] pub enum Volume: u16 { 0 => Quiet, 1 => Normal, 2 => Loud, } } // EOF