add Sounds reader
parent
a8f04cc01c
commit
58cbdd1a57
|
@ -426,6 +426,9 @@ actual PCM data.
|
||||||
|
|
||||||
All integers here are big-endian unless specified.
|
All integers here are big-endian unless specified.
|
||||||
|
|
||||||
|
All unspecified bytes must be set to `0` when written, although when reading
|
||||||
|
should not be checked as they may be garbage.
|
||||||
|
|
||||||
The type "`fixed`" refers to a 32-bit fixed point number with the format 15.16s
|
The type "`fixed`" refers to a 32-bit fixed point number with the format 15.16s
|
||||||
(the lower 16 bits are fractional, the upper 15 are integral, and one bit for
|
(the lower 16 bits are fractional, the upper 15 are integral, and one bit for
|
||||||
sign.)
|
sign.)
|
||||||
|
@ -1079,10 +1082,32 @@ but due to padding, the real length is 34.
|
||||||
- `SndBeg`, `SndKey` and `SndEnd` are the sounds played at the first, key and
|
- `SndBeg`, `SndKey` and `SndEnd` are the sounds played at the first, key and
|
||||||
last frame of this sequence.
|
last frame of this sequence.
|
||||||
|
|
||||||
|
### Bitmap Header ###
|
||||||
|
|
||||||
|
Bitmap Header is 26 bytes.
|
||||||
|
|
||||||
|
Each Bitmap Header is followed by either `Height * 4` or `Width * 4` empty
|
||||||
|
bytes which must be skipped.
|
||||||
|
|
||||||
|
| Name | Type | Offset |
|
||||||
|
| ---- | ---- | ------ |
|
||||||
|
| `Width` | `u16` | `0` |
|
||||||
|
| `Height` | `u16` | `2` |
|
||||||
|
| `Pitch` | `u16` | `4` |
|
||||||
|
| `Flags` | `u16` | `6` |
|
||||||
|
| `Depth` | `u16` | `8` |
|
||||||
|
|
||||||
|
- `Width` is the number of pixels on the horizontal axis.
|
||||||
|
- `Height` is the number of pixels on the vertical axis.
|
||||||
|
- `Pitch` is either the number of pixels per row if row ordered, per column if
|
||||||
|
column ordered, or `65535` if the data is transparency RLE compressed.
|
||||||
|
- `Flags` is a Bitmap Flags bit field.
|
||||||
|
- `Depth` must always be `8`.
|
||||||
|
|
||||||
## Sounds ##
|
## Sounds ##
|
||||||
|
|
||||||
Sounds files start with a header followed by all of the actual sound
|
Sounds files start with a header followed by all of the actual sound
|
||||||
definitions. Simple enough.
|
definitions. Each sound starts with a Carbon Sound Header.
|
||||||
|
|
||||||
### Sounds Header ###
|
### Sounds Header ###
|
||||||
|
|
||||||
|
@ -1113,24 +1138,69 @@ Sound Definition is 64 bytes.
|
||||||
|
|
||||||
| Name | Type | Offset |
|
| Name | Type | Offset |
|
||||||
| ---- | ---- | ------ |
|
| ---- | ---- | ------ |
|
||||||
| sound_code | `u16` | `0` |
|
| `Code` | `u16` | `0` |
|
||||||
| `Behaviour` | `u16` | `2` |
|
| `Volume` | `u16` | `2` |
|
||||||
| `Flags` | `u16` | `4` |
|
| `Flags` | `u16` | `4` |
|
||||||
| `Chance` | `u16` | `6` |
|
| `Chance` | `u16` | `6` |
|
||||||
| `PitchLo` | `fixed` | `8` |
|
| `PitchLo` | `fixed` | `8` |
|
||||||
| `PitchHi` | `fixed` | `12` |
|
| `PitchHi` | `fixed` | `12` |
|
||||||
| permutations | `i16` | `16` |
|
| `NumOfs` | `u16` | `16` |
|
||||||
| perms_played | `u16` | `18` |
|
| `GroupOffset` | `u32` | `20` |
|
||||||
| group_offset | `u32` | `20` |
|
| `Size` | `u32` | `24` |
|
||||||
| single_length | `u32` | `24` |
|
| `GroupSize` | `u32` | `28` |
|
||||||
| total_length | `u32` | `28` |
|
| `AddOffset` | `u32[5]` | `32` |
|
||||||
| sound_offsets | `u32[5]` | `32` |
|
|
||||||
|
|
||||||
- `Behaviour` is a Sound Behaviour enumeration.
|
- `Code` is an Object ID referencing something (TODO.)
|
||||||
|
- `Volume` is a Sound Behaviour enumeration.
|
||||||
- `Flags` is a Sound Definition Flags bit field.
|
- `Flags` is a Sound Definition Flags bit field.
|
||||||
- `Chance` is the chance out of `65535` that the sound will not play.
|
- `Chance` is the chance out of `65535` that the sound will not play.
|
||||||
- `PitchLo` is the lower random pitch bound, if `0` then it will be `1.0`.
|
- `PitchLo` is the lower random pitch bound, if `0` then it will be `1.0`.
|
||||||
- `PitchHi` is the high random pitch bound, if `0` then it will be `PitchLo`.
|
- `PitchHi` is the high random pitch bound, if `0` then it will be `PitchLo`.
|
||||||
|
- `NumOfs` is the number of random sounds to pick from `AddOffset`.
|
||||||
|
- `GroupOffset` is the starting offset for each additive sound offset.
|
||||||
|
- `Size` is the sound of an individual sound in the group.
|
||||||
|
- `GroupSize` is the total size of all sounds in the group.
|
||||||
|
- `AddOffset` is the offset added to `GroupOffset` to get an individual sound.
|
||||||
|
While it is an array of `NumOfs` offsets, it has a fixed size in the format.
|
||||||
|
|
||||||
|
### Carbon Sound Header ###
|
||||||
|
|
||||||
|
Carbon Sound Header is 21 bytes.
|
||||||
|
|
||||||
|
The sound format is from Carbon's `SoundHeader` structures. It's used primarily
|
||||||
|
in System 7 programs as `snd` resources but in OS X it was deprecated in favor
|
||||||
|
of QuickTime. HFS still has Resource Forks but they aren't used anymore. I
|
||||||
|
don't imagine this format was ever used for anything else, except for Marathon,
|
||||||
|
which embeds it in the Sound files directly, instead of using `snd` resources
|
||||||
|
(which have a larger structure consisting of a resource header and sound
|
||||||
|
commands rather than just the header and sample data.)
|
||||||
|
|
||||||
|
| Name | Type | Offset |
|
||||||
|
| ---- | ---- | ------ |
|
||||||
|
| `Size` | `u32` | `4` |
|
||||||
|
| `SampleRate` | `u16` | `8` |
|
||||||
|
| `LoopBeg` | `u32` | `12` |
|
||||||
|
| `LoopEnd` | `u32` | `16` |
|
||||||
|
| `Magic` | `u8` | `20` |
|
||||||
|
|
||||||
|
- If `Magic` is `$00` nothing else needs to be done and raw signed 8-bit mono
|
||||||
|
PCM sample data starts at byte 22. If it is `$FF` it is followed by a Carbon
|
||||||
|
Extended Sound Header, or if it is `$FE` it is followed by a Carbon Compressed
|
||||||
|
Sound Header. The compressed sound header is not documented because it is not
|
||||||
|
actually used by Marathon.
|
||||||
|
|
||||||
|
### Carbon Extended Sound Header ###
|
||||||
|
|
||||||
|
Carbon Extended Sound Header is 42 bytes.
|
||||||
|
|
||||||
|
The extended sound header contains more useless information and even several
|
||||||
|
fields that do absolutely nothing. Wow. At least it can store 16 bit samples.
|
||||||
|
It also has an 80-bit float in it, which horrifies me greatly. There's only one
|
||||||
|
actually useful field.
|
||||||
|
|
||||||
|
| Name | Type | Offset |
|
||||||
|
| ---- | ---- | ------ |
|
||||||
|
| `SampleBits` | `u16` | `26` |
|
||||||
|
|
||||||
# ENUMERATIONS ################################################################
|
# ENUMERATIONS ################################################################
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub mod crc;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod fixed;
|
pub mod fixed;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
pub mod sound;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
//! Sound representation.
|
||||||
|
|
||||||
|
use crate::durandal::err::*;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
pub fn write_wav(out: &mut impl io::Write, snd: &impl Sound) -> ResultS<()>
|
||||||
|
{
|
||||||
|
let rate = u32::from(snd.rate());
|
||||||
|
let bps = rate * 2;
|
||||||
|
let ssize = bps * snd.len() as u32;
|
||||||
|
let fsize = 36 + ssize;
|
||||||
|
|
||||||
|
out.write_all(b"RIFF")?;
|
||||||
|
out.write_all(&fsize.to_le_bytes())?;
|
||||||
|
out.write_all(b"WAVE")?;
|
||||||
|
out.write_all(b"fmt ")?;
|
||||||
|
out.write_all(&16u32.to_le_bytes())?;
|
||||||
|
out.write_all(&1u16.to_le_bytes())?; // PCM
|
||||||
|
out.write_all(&1u16.to_le_bytes())?; // mono
|
||||||
|
out.write_all(&rate.to_le_bytes())?; // rate
|
||||||
|
out.write_all(&bps.to_le_bytes())?; // bytes per second
|
||||||
|
out.write_all(&2u16.to_le_bytes())?; // block alignment
|
||||||
|
out.write_all(&16u16.to_le_bytes())?; // bits per sample
|
||||||
|
out.write_all(b"data")?;
|
||||||
|
out.write_all(&ssize.to_le_bytes())?;
|
||||||
|
|
||||||
|
for p in 0..snd.len() {
|
||||||
|
let sample = snd.index(p);
|
||||||
|
out.write_all(&sample.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ssize & 1 == 1 {
|
||||||
|
out.write_all(&[0])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Sound
|
||||||
|
{
|
||||||
|
fn rate(&self) -> u16;
|
||||||
|
fn len(&self) -> usize;
|
||||||
|
fn index(&self, p: usize) -> i16;
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool
|
||||||
|
{
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, p: usize) -> Option<i16>
|
||||||
|
{
|
||||||
|
if p < self.len() {
|
||||||
|
Some(self.index(p))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sound8
|
||||||
|
{
|
||||||
|
/// Creates a new Sound8.
|
||||||
|
pub fn new(rate: u16, len: usize) -> Self
|
||||||
|
{
|
||||||
|
Self{rate, data: Vec::with_capacity(len)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sound16
|
||||||
|
{
|
||||||
|
/// Creates a new Sound16.
|
||||||
|
pub fn new(rate: u16, len: usize) -> Self
|
||||||
|
{
|
||||||
|
Self{rate, data: Vec::with_capacity(len)}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Sound16 from an unsigned 8-bit stream.
|
||||||
|
pub fn new_from_8(rate: u16, b: &[u8]) -> Self
|
||||||
|
{
|
||||||
|
let mut snd = Sound16::new(rate, b.len());
|
||||||
|
|
||||||
|
for &sample in b {
|
||||||
|
snd.data.push(Sound16::sample_from_8(sample));
|
||||||
|
}
|
||||||
|
|
||||||
|
snd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Sound16 from a signed 16-bit stream.
|
||||||
|
pub fn new_from_16(rate: u16, b: &[u8]) -> Self
|
||||||
|
{
|
||||||
|
let mut snd = Sound16::new(rate, b.len() / 2);
|
||||||
|
|
||||||
|
for (&x, &y) in b.iter().step_by(2).zip(b.iter().step_by(2).next()) {
|
||||||
|
snd.data.push(i16::from_be_bytes([x, y]));
|
||||||
|
}
|
||||||
|
|
||||||
|
snd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a signed 16-bit sample from an unsigned 8-bit sample.
|
||||||
|
pub fn sample_from_8(sample: u8) -> i16
|
||||||
|
{
|
||||||
|
i16::from(sample) - 0x80 << 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sound for Sound8
|
||||||
|
{
|
||||||
|
fn rate(&self) -> u16 {self.rate}
|
||||||
|
fn len(&self) -> usize {self.data.len()}
|
||||||
|
|
||||||
|
fn index(&self, p: usize) -> i16
|
||||||
|
{
|
||||||
|
Sound16::sample_from_8(self.data[p])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sound for Sound16
|
||||||
|
{
|
||||||
|
fn rate(&self) -> u16 {self.rate}
|
||||||
|
fn len(&self) -> usize {self.data.len()}
|
||||||
|
fn index(&self, p: usize) -> i16 {self.data[p]}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sound8
|
||||||
|
{
|
||||||
|
rate: u16,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sound16
|
||||||
|
{
|
||||||
|
rate: u16,
|
||||||
|
pub data: Vec<i16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
37
src/main.rs
37
src/main.rs
|
@ -1,5 +1,5 @@
|
||||||
use maraiah::{durandal::{bin::*, chunk::*, err::*, file::*, image::*, text::*},
|
use maraiah::{durandal::{bin::*, err::*, file::*, image::*, sound::*, text::*},
|
||||||
marathon::{machdr, map, pict, shp, term, wad}};
|
marathon::{machdr, map, pict, shp, snd, term, wad}};
|
||||||
use std::{collections::HashSet,
|
use std::{collections::HashSet,
|
||||||
fs,
|
fs,
|
||||||
io::{self, Write}};
|
io::{self, Write}};
|
||||||
|
@ -31,6 +31,12 @@ fn make_yaml<T>(opt: &Options, data: &T) -> ResultS<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_wav(fname: &str, snd: &impl Sound) -> ResultS<()>
|
||||||
|
{
|
||||||
|
let mut out = io::BufWriter::new(fs::File::create(fname)?);
|
||||||
|
write_wav(&mut out, snd)
|
||||||
|
}
|
||||||
|
|
||||||
fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||||
{
|
{
|
||||||
if opt.wad_all {
|
if opt.wad_all {
|
||||||
|
@ -44,12 +50,12 @@ fn dump_chunk(opt: &Options, cid: Ident, cnk: &[u8], eid: u16) -> ResultS<()>
|
||||||
let im = pict::load_pict(cnk)?;
|
let im = pict::load_pict(cnk)?;
|
||||||
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
|
make_tga(&format!("{}/pict_{}.tga", opt.out_dir, eid), &im)?;
|
||||||
}
|
}
|
||||||
b"Minf" => make_yaml(opt, &map::Minf::chunk(cnk)?)?,
|
b"Minf" => make_yaml(opt, &map::read_minf(cnk)?)?,
|
||||||
b"EPNT" => make_yaml(opt, &map::Endpoint::chunk(cnk)?)?,
|
b"EPNT" => make_yaml(opt, &c_array::<map::Endpoint>(cnk)?)?,
|
||||||
b"PNTS" => make_yaml(opt, &map::Point::chunk(cnk)?)?,
|
b"PNTS" => make_yaml(opt, &c_array::<map::Point>(cnk)?)?,
|
||||||
b"LINS" => make_yaml(opt, &map::Line::chunk(cnk)?)?,
|
b"LINS" => make_yaml(opt, &c_array::<map::Line>(cnk)?)?,
|
||||||
b"SIDS" => make_yaml(opt, &map::Side::chunk(cnk)?)?,
|
b"SIDS" => make_yaml(opt, &c_array::<map::Side>(cnk)?)?,
|
||||||
b"term" => make_yaml(opt, &term::Terminal::chunk(cnk)?)?,
|
b"term" => make_yaml(opt, &term::read_term(cnk)?)?,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,6 +135,20 @@ fn process_shp(opt: &Options, b: &[u8]) -> ResultS<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_snd(_opt: &Options, b: &[u8]) -> ResultS<()>
|
||||||
|
{
|
||||||
|
for (c, st) in snd::read_sounds(b)?.iter().enumerate() {
|
||||||
|
for (k, sd) in st {
|
||||||
|
for (i, snd) in sd.sounds.iter().enumerate() {
|
||||||
|
let fname = format!("out/snd{}_{}_{}.wav", c, k, i);
|
||||||
|
make_wav(&fname, snd)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> ResultS<()>
|
fn main() -> ResultS<()>
|
||||||
{
|
{
|
||||||
use argparse::*;
|
use argparse::*;
|
||||||
|
@ -211,6 +231,7 @@ fn main() -> ResultS<()>
|
||||||
match typ {
|
match typ {
|
||||||
"wad:" => process_wad(&opt, b),
|
"wad:" => process_wad(&opt, b),
|
||||||
"shp:" => process_shp(&opt, b),
|
"shp:" => process_shp(&opt, b),
|
||||||
|
"snd:" => process_snd(&opt, b),
|
||||||
_ => Err(err_msg("invalid file type specified on commandline")),
|
_ => Err(err_msg("invalid file type specified on commandline")),
|
||||||
}?;
|
}?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ pub mod machdr;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
pub mod pict;
|
pub mod pict;
|
||||||
pub mod shp;
|
pub mod shp;
|
||||||
|
pub mod snd;
|
||||||
pub mod term;
|
pub mod term;
|
||||||
pub mod wad;
|
pub mod wad;
|
||||||
pub mod xfer;
|
pub mod xfer;
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
//! 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 loop_beg = c_u32b(b, 12)?;
|
||||||
|
let loop_end = c_u32b(b, 16)?;
|
||||||
|
let magic = c_byte(b, 20)?;
|
||||||
|
|
||||||
|
match magic {
|
||||||
|
0 => Ok(Sound16::new_from_8(rate, c_data(b, 22..22 + len)?)),
|
||||||
|
0xFF => {
|
||||||
|
let stream = c_data(b, 63..63 + len)?;
|
||||||
|
match c_u16b(b, 47)? {
|
||||||
|
16 => Ok(Sound16::new_from_16(rate, stream)),
|
||||||
|
_ => Ok(Sound16::new_from_8(rate, 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 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, 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 chance: u16,
|
||||||
|
pub pitch_lo: Fixed,
|
||||||
|
pub pitch_hi: Fixed,
|
||||||
|
pub sounds: Vec<Sound16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type SoundTable = HashMap<u16, SoundDef>;
|
||||||
|
|
||||||
|
c_enum! {
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub enum Volume: u16
|
||||||
|
{
|
||||||
|
0 => Quiet,
|
||||||
|
1 => Normal,
|
||||||
|
2 => Loud,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
Loading…
Reference in New Issue