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