Initial commit
This commit is contained in:
commit
b48fa03eaa
|
@ -0,0 +1,32 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vocnom"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "vocnom"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom = "7"
|
|
@ -0,0 +1,240 @@
|
|||
use nom::bytes::streaming::tag;
|
||||
use nom::number;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Codec {
|
||||
Pcm8BitUnsigned,
|
||||
Adpcm4to8,
|
||||
Adpcm3to8,
|
||||
Adpcm2to8,
|
||||
Pcm16BitSigned,
|
||||
Alaw,
|
||||
Ulaw,
|
||||
Adpcm4to16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BlockType {
|
||||
Terminator,
|
||||
SoundData {
|
||||
sample_rate: u32,
|
||||
codec: Codec,
|
||||
data: Vec<u8>,
|
||||
},
|
||||
SoundDataContinuation {
|
||||
data: Vec<u8>,
|
||||
},
|
||||
Silence {
|
||||
length: u16,
|
||||
sample_rate: u32,
|
||||
},
|
||||
Marker {
|
||||
value: u16,
|
||||
},
|
||||
Text {
|
||||
data: Vec<u8>,
|
||||
},
|
||||
RepeatStart {
|
||||
count: u16,
|
||||
},
|
||||
RepeatEnd,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Version {
|
||||
major: u8,
|
||||
minor: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Checksum {
|
||||
value: u16,
|
||||
valid: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Voc {
|
||||
version: Version,
|
||||
checksum: Checksum,
|
||||
blocks: Vec<BlockType>,
|
||||
}
|
||||
|
||||
pub fn parse_block(input: &[u8]) -> nom::IResult<&[u8], BlockType> {
|
||||
let (input, block_type) = number::streaming::le_u8(input)?;
|
||||
match block_type {
|
||||
0 => Ok((input, BlockType::Terminator)),
|
||||
1 => {
|
||||
let (input, block_size) = number::streaming::le_u24(input)?;
|
||||
let (input, frequency_divisor) = number::streaming::le_u8(input)?;
|
||||
let (input, codec_id) = number::streaming::le_u8(input)?;
|
||||
let sample_rate: u32 = 1000000_u32 / (256_u32 - frequency_divisor as u32);
|
||||
let codec = match codec_id {
|
||||
0 => Codec::Pcm8BitUnsigned,
|
||||
1 => Codec::Adpcm4to8,
|
||||
2 => Codec::Adpcm3to8,
|
||||
3 => Codec::Adpcm2to8,
|
||||
4 => Codec::Pcm16BitSigned,
|
||||
5 => Codec::Alaw,
|
||||
6 => Codec::Ulaw,
|
||||
7 => Codec::Adpcm4to16,
|
||||
_ => panic!("Invalid sound format"),
|
||||
};
|
||||
let (input, data) = nom::bytes::complete::take(block_size - 2)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
BlockType::SoundData {
|
||||
sample_rate,
|
||||
codec,
|
||||
data: data.to_vec(),
|
||||
},
|
||||
))
|
||||
}
|
||||
2 => {
|
||||
let (input, block_size) = number::streaming::le_u24(input)?;
|
||||
let (input, data) = nom::bytes::complete::take(block_size)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
BlockType::SoundDataContinuation {
|
||||
data: data.to_vec(),
|
||||
},
|
||||
))
|
||||
}
|
||||
3 => {
|
||||
let (input, _) = number::streaming::le_u16(input)?;
|
||||
let (input, length) = number::streaming::le_u16(input)?;
|
||||
let (input, frequency_divisor) = number::streaming::le_u8(input)?;
|
||||
let sample_rate: u32 = 1000000_u32 / (256_u32 - frequency_divisor as u32);
|
||||
Ok((
|
||||
input,
|
||||
BlockType::Silence {
|
||||
length,
|
||||
sample_rate,
|
||||
},
|
||||
))
|
||||
}
|
||||
4 => {
|
||||
let (input, _) = number::streaming::le_u16(input)?;
|
||||
let (input, value) = number::streaming::le_u16(input)?;
|
||||
Ok((input, BlockType::Marker { value }))
|
||||
}
|
||||
5 => {
|
||||
let (input, block_size) = number::streaming::le_u24(input)?;
|
||||
let (input, data) = nom::bytes::complete::take(block_size)(input)?;
|
||||
Ok((
|
||||
input,
|
||||
BlockType::Text {
|
||||
data: data.to_vec(),
|
||||
},
|
||||
))
|
||||
}
|
||||
6 => {
|
||||
let (input, _) = number::streaming::le_u24(input)?;
|
||||
let (input, count) = number::streaming::le_u16(input)?;
|
||||
Ok((input, BlockType::RepeatStart { count }))
|
||||
}
|
||||
7 => Ok((input, BlockType::RepeatEnd)),
|
||||
_ => panic!("Invalid block type"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_voc(input: &[u8]) -> nom::IResult<&[u8], Voc> {
|
||||
let (input, _) = tag("Creative Voice File")(input)?;
|
||||
let (input, _) = tag(&[0x1a, 0x1a, 0])(input)?;
|
||||
let (input, version_minor) = number::streaming::le_u8(input)?;
|
||||
let (input, version_major) = number::streaming::le_u8(input)?;
|
||||
let (input, checksum) = number::streaming::le_u16(input)?;
|
||||
// Calculate checksum from version
|
||||
let checksum2 = (!i16::from_le_bytes([version_minor, version_major]) + 0x1234) as u16;
|
||||
let valid = checksum == checksum2;
|
||||
|
||||
let mut blocks = Vec::new();
|
||||
let mut remaining_input = input;
|
||||
|
||||
loop {
|
||||
match parse_block(remaining_input) {
|
||||
Ok((input, block)) => {
|
||||
blocks.push(block);
|
||||
remaining_input = input;
|
||||
}
|
||||
Err(nom::Err::Incomplete(_)) => {
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
input,
|
||||
Voc {
|
||||
version: Version {
|
||||
major: version_major,
|
||||
minor: version_minor,
|
||||
},
|
||||
checksum: Checksum {
|
||||
value: checksum,
|
||||
valid,
|
||||
},
|
||||
blocks,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let f = include_bytes!("../EDEN.MUS");
|
||||
match parse_voc(f) {
|
||||
Ok((_, voc)) => {
|
||||
println!("Version: {}.{}", voc.version.major, voc.version.minor);
|
||||
println!(
|
||||
"Checksum: {}, Value: {:#0x} ",
|
||||
voc.checksum.valid, voc.checksum.value
|
||||
);
|
||||
for (i, block) in voc.blocks.iter().enumerate() {
|
||||
print!("#{} - ", i);
|
||||
match block {
|
||||
BlockType::Terminator => println!("Terminator"),
|
||||
BlockType::SoundData {
|
||||
sample_rate,
|
||||
codec,
|
||||
data,
|
||||
} => {
|
||||
println!(
|
||||
"Sound data: sample rate = {}, codec = {:?}, size = {}",
|
||||
sample_rate,
|
||||
codec,
|
||||
data.len()
|
||||
)
|
||||
}
|
||||
BlockType::SoundDataContinuation { data } => {
|
||||
println!("Sound data continuation: size = {}", data.len())
|
||||
}
|
||||
BlockType::Silence {
|
||||
length,
|
||||
sample_rate,
|
||||
} => {
|
||||
println!(
|
||||
"Silence: length = {}, sample rate = {}",
|
||||
length, sample_rate
|
||||
)
|
||||
}
|
||||
BlockType::Marker { value } => {
|
||||
println!("Marker: value = {}", value)
|
||||
}
|
||||
BlockType::Text { data } => {
|
||||
println!("Text: size = {}", data.len())
|
||||
}
|
||||
BlockType::RepeatStart { count } => {
|
||||
println!("Repeat: repetitions = {}", count)
|
||||
}
|
||||
BlockType::RepeatEnd => {
|
||||
println!("End repeat")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error parsing VOC file: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue