//! 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] usize; rate = u16[8]; lp_beg = u32[12] usize; lp_end = u32[16] 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] 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] usize; grp_ofs = u32[20] 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 + usize_from_u32(u32b(&b[p..]))); p += 4; } let sounds = Vec::with_capacity(n_sounds); Ok(Some((ofs, index, SoundDef{volume, flags, chance, pitch_lo, pitch_hi, 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] usize; snd_num = u16[10] usize; } if version != 1 || magic.0 != *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) } /// A sound definition containing one, many or no sounds. pub struct SoundDef { /// The volume type for this sound. pub volume: Volume, /// The flags for this sound. pub flags: SoundFlags, /// The chance out of `u16::max_value()` that this sound will not play. pub chance: u16, /// The low random pitch bound. pub pitch_lo: Fixed, /// The high random pitch bound. pub pitch_hi: Fixed, /// All of the sounds in this collection. pub sounds: Vec, } /// A table of sound definitions. pub type SoundTable = BTreeMap; bitflags! { /// Flags for `SoundDef`. #[derive(serde::Serialize)] pub struct SoundFlags: u16 { /// The sound will not restart when trying to play over itself. const NO_RESTART = 1; /// The sound will not switch channels when trying to play over itself. const NO_CHANNEL_SWITCH = 1 << 1; /// The pitch variance will be halved. const LESS_PITCH_CHANGE = 1 << 2; /// The pitch variance will be nullified. const NO_PITCH_CHANGE = 1 << 3; /// The sound will play even when completely obstructed by walls. const NO_OBSTRUCTION = 1 << 4; /// The sound will play even when completely obstructed by media. const NO_MEDIA_OBSTRUCT = 1 << 5; /// The sound will have special stereo effects. 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