Initial commit

This commit is contained in:
Pedro de Oliveira 2023-04-28 01:46:27 +01:00
commit b48fa03eaa
4 changed files with 281 additions and 0 deletions

32
Cargo.lock generated Normal file
View File

@ -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",
]

9
Cargo.toml Normal file
View File

@ -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"

BIN
EDEN.MUS Normal file

Binary file not shown.

240
src/main.rs Normal file
View File

@ -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);
}
}
}