Maraiah/source/marathon/snd.rs

185 lines
4.3 KiB
Rust

//! 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<Sound16>
{
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<Option<(Vec<usize>, 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<Vec<SoundTable>>
{
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<Sound16>,
}
/// A table of sound definitions.
pub type SoundTable = BTreeMap<u16, SoundDef>;
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