145 lines
3.4 KiB
Rust
145 lines
3.4 KiB
Rust
//! Marathon Sounds format handling.
|
|
|
|
use crate::durandal::{bin::*, err::*, fixed::*, sound::*};
|
|
use bitflags::bitflags;
|
|
use serde::Serialize;
|
|
use std::collections::HashMap;
|
|
|
|
fn sound(b: &[u8]) -> ResultS<Sound16>
|
|
{
|
|
let len = c_u32b(b, 4)? as usize;
|
|
let rate = c_u16b(b, 8)?;
|
|
let lp_beg = c_u32b(b, 12)? as usize;
|
|
let lp_end = c_u32b(b, 16)? as usize;
|
|
let magic = c_byte(b, 20)?;
|
|
|
|
match magic {
|
|
0 => {
|
|
let stream = c_data(b, 22..22 + len)?;
|
|
Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream))
|
|
}
|
|
0xFF => {
|
|
let len = c_u32b(b, 22)? as usize;
|
|
match c_u16b(b, 48)? {
|
|
16 => {
|
|
let stream = c_data(b, 63..63 + len * 2)?;
|
|
Ok(Sound16::new_from_16(rate, lp_beg, lp_end, stream))
|
|
}
|
|
_ => {
|
|
let stream = c_data(b, 63..63 + len)?;
|
|
Ok(Sound16::new_from_8(rate, lp_beg, lp_end, stream))
|
|
}
|
|
}
|
|
}
|
|
_ => bail!("invalid magic number"),
|
|
}
|
|
}
|
|
|
|
fn sound_def(b: &[u8]) -> ResultS<Option<(Vec<usize>, u16, SoundDef)>>
|
|
{
|
|
let index = c_u16b(b, 0)?;
|
|
let volume = c_u16b(b, 2)?;
|
|
let flags = c_u16b(b, 4)?;
|
|
let chance = c_u16b(b, 6)?;
|
|
let pitch_lo = c_u32b(b, 8)?;
|
|
let pitch_hi = c_u32b(b, 12)?;
|
|
let n_sounds = c_u16b(b, 16)? as usize;
|
|
let grp_ofs = c_u32b(b, 20)? as usize;
|
|
let volume = Volume::from_repr(volume)?;
|
|
let flags = ok!(SoundFlags::from_bits(flags), "bad SoundFlags")?;
|
|
let pitch_lo = Fixed::from_bits(pitch_lo);
|
|
let pitch_hi = Fixed::from_bits(pitch_hi);
|
|
|
|
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 + c_u32b(b, p)? as usize);
|
|
p += 4;
|
|
}
|
|
|
|
Ok(Some((ofs, index, SoundDef{volume, flags, chance, pitch_lo, pitch_hi,
|
|
sounds: Vec::with_capacity(n_sounds)})))
|
|
}
|
|
|
|
pub fn read_sounds(b: &[u8]) -> ResultS<Vec<SoundTable>>
|
|
{
|
|
let version = c_u32b(b, 0)?;
|
|
let magic = c_iden(b, 4)?;
|
|
let src_num = c_u16b(b, 8)? as usize; // TODO
|
|
let snd_num = c_u16b(b, 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 = HashMap::with_capacity(snd_num);
|
|
|
|
for _ in 0..snd_num {
|
|
if let Some((ofs, idx, mut def)) = sound_def(c_data(b, p..p + 64)?)? {
|
|
for &ofs in &ofs {
|
|
def.sounds.push(sound(c_data(b, ofs..)?)?);
|
|
}
|
|
|
|
st.insert(idx, def);
|
|
}
|
|
|
|
p += 64;
|
|
}
|
|
|
|
sc.push(st);
|
|
}
|
|
|
|
Ok(sc)
|
|
}
|
|
|
|
pub struct SoundDef
|
|
{
|
|
pub volume: Volume,
|
|
pub flags: SoundFlags,
|
|
pub chance: u16,
|
|
pub pitch_lo: Fixed,
|
|
pub pitch_hi: Fixed,
|
|
pub sounds: Vec<Sound16>,
|
|
}
|
|
|
|
type SoundTable = HashMap<u16, SoundDef>;
|
|
|
|
bitflags! {
|
|
#[derive(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! {
|
|
#[derive(Debug, Serialize)]
|
|
pub enum Volume: u16
|
|
{
|
|
0 => Quiet,
|
|
1 => Normal,
|
|
2 => Loud,
|
|
}
|
|
}
|
|
|
|
// EOF
|