diff --git a/Cargo.lock b/Cargo.lock index 2dd9dcf..656e77b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,6 +160,28 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +[[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 = "once_cell" version = "1.17.1" @@ -192,11 +214,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "vocnom" +version = "0.1.0" +source = "git+https://git.deadbsd.org/falso/vocnom.git#4b0e58bb2c5d0465b78f80653edbe5b194d59d22" +dependencies = [ + "nom", +] + [[package]] name = "voctool" version = "0.1.0" dependencies = [ "clap", + "vocnom", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 007691a..8ce5062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] clap = { version = "4.0.22", features = ["cargo"] } +vocnom = { git = "https://git.deadbsd.org/falso/vocnom.git" } diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index dbbab18..0000000 --- a/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod voc; diff --git a/src/main.rs b/src/main.rs index eadc5fe..9e61629 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ -use voctool::*; +use std::fs; +use vocnom::reader::parse_voc; fn main() { let cmd = clap::Command::new(env!("CARGO_CRATE_NAME")) @@ -31,24 +32,18 @@ fn main() { match matches.subcommand() { Some(("info", arguments)) => { let filename = arguments.get_one::("file").unwrap(); - let voc = voc::VocFile::from_file(filename); + let contents = fs::read(filename).unwrap(); + let bytes = contents.as_slice(); + let (_, voc) = parse_voc(bytes).unwrap(); println!( "Version {}.{} - Checksum: {}", - voc.version.0, - voc.version.1, - if voc.checksum { "Valid!" } else { "Invalid!" } + voc.version.major, + voc.version.minor, + if voc.checksum.valid { "Valid!" } else { "Invalid!" } ); for (n, block) in voc.blocks.into_iter().enumerate() { - if let Some(block_info) = block.get_info() { - println!( - "Block #{} - Start Address: {:02X?} - End Address: {:02X?} - Size {}", - n, - block_info.start_address, - block_info.end_address, - block_info.get_size() - ); - println!("{:?}", block); - } + print!("Block #{} - ", n); + block.info(); } } Some(("extract", _arguments)) => { diff --git a/src/voc/blocks.rs b/src/voc/blocks.rs deleted file mode 100644 index 89f73db..0000000 --- a/src/voc/blocks.rs +++ /dev/null @@ -1,537 +0,0 @@ -use std::fmt; -use std::fs::File; -use std::io::{Read, Seek}; - -#[derive(Clone, Copy, Debug)] -pub enum Codec { - Pcm8BitUnsigned, - Adpcm4to8, - Adpcm3to8, - Adpcm2to8, - Pcm16BitSigned, - Alaw, - Ulaw, - Adpcm4to16, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum BlockType { - Terminator, - SoundData, - SoundDataContinuation, - Silence, - Marker, - Text, - RepeatStart, - RepeatEnd, -} - -pub type BlockT = Box; - -pub trait Block: fmt::Debug { - fn to_bytes(&self) -> Vec; - fn get_info(&self) -> &Option; -} - -pub struct BlockInfo { - pub block_type: BlockType, - pub start_address: u64, - pub end_address: u64, -} - -impl BlockInfo { - pub fn get_size(&self) -> u64 { - self.end_address - self.start_address - } -} - -// Terminator - -pub struct Terminator { - info: Option, - block_type: BlockType, -} - -impl Terminator { - #[cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] - pub fn new() -> Self { - Self { - info: None, - block_type: BlockType::Terminator, - } - } - pub fn from_stream(_file: &mut File, start_address: u64, end_address: u64) -> Self { - Self { - info: Some(BlockInfo { - block_type: BlockType::Terminator, - start_address, - end_address, - }), - block_type: BlockType::Terminator, - } - } -} - -impl Block for Terminator { - fn to_bytes(&self) -> Vec { - vec![self.block_type as u8] - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for Terminator { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Terminator").finish() - } -} - -// SoundData - -pub struct SoundData { - info: Option, - block_type: BlockType, - sample_rate: u32, - codec: Codec, - data: Vec, -} - -impl SoundData { - pub fn new(sample_rate: u32, codec: Codec, data: Vec) -> Self { - Self { - info: None, - block_type: BlockType::SoundData, - sample_rate, - codec, - data, - } - } - - pub fn from_stream(file: &mut File, start_address: u64, end_address: u64) -> Self { - let mut frequency_divisor_buffer: [u8; 1] = [0]; - file.read_exact(&mut frequency_divisor_buffer).unwrap(); - let sample_rate: u32 = 1000000_u32 / (256_u32 - frequency_divisor_buffer[0] as u32); - - let mut codec_buffer: [u8; 1] = [0]; - file.read_exact(&mut codec_buffer).unwrap(); - let codec = match codec_buffer[0] { - 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!("Bad Sound format. Got {}", codec_buffer[0]), - }; - - let address = file.stream_position().unwrap(); - let mut data: Vec = vec![0; (end_address - address) as usize]; - file.read_exact(&mut data).unwrap(); - - Self { - info: Some(BlockInfo { - block_type: BlockType::SoundData, - start_address, - end_address, - }), - block_type: BlockType::SoundData, - sample_rate, - codec, - data, - } - } -} - -impl Block for SoundData { - fn to_bytes(&self) -> Vec { - let size_bytes: [u8; 4] = (self.data.len() as u32 + 2).to_le_bytes(); - let frequency_divisor: u8 = (256_u32 - 1000000_u32 / self.sample_rate) as u8; - - let mut result: Vec = vec![ - self.block_type as u8, - size_bytes[0], - size_bytes[1], - size_bytes[2], - frequency_divisor, - self.codec as u8, - ]; - - result.extend_from_slice(&self.data); - - result - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for SoundData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SoundData") - .field("size", &self.data.len()) - .field("sample rate", &self.sample_rate) - .field("codec", &self.codec) - .finish() - } -} - -// SoundDataContinuation - -pub struct SoundDataContinuation { - info: Option, - block_type: BlockType, - data: Vec, -} - -impl SoundDataContinuation { - pub fn new(data: Vec) -> Self { - Self { - info: None, - block_type: BlockType::SoundDataContinuation, - data, - } - } - pub fn from_stream(file: &mut File, start_address: u64, end_address: u64) -> Self { - let address = file.stream_position().unwrap(); - let mut data: Vec = vec![0; (end_address - address) as usize]; - file.read_exact(&mut data).unwrap(); - - Self { - info: Some(BlockInfo { - block_type: BlockType::SoundDataContinuation, - start_address, - end_address, - }), - block_type: BlockType::SoundDataContinuation, - data, - } - } -} - -impl Block for SoundDataContinuation { - fn to_bytes(&self) -> Vec { - let size_bytes: [u8; 4] = (self.data.len() as u32).to_le_bytes(); - let mut result: Vec = vec![ - self.block_type as u8, - size_bytes[0], - size_bytes[1], - size_bytes[2], - ]; - - result.extend_from_slice(&self.data); - result - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for SoundDataContinuation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SoundDataContinuation") - .field("size", &self.data.len()) - .finish() - } -} - -// Silence - -pub struct Silence { - info: Option, - block_type: BlockType, - length: u16, - sample_rate: u32, -} - -impl Silence { - pub fn new(length: u16, sample_rate: u32) -> Self { - Self { - info: None, - block_type: BlockType::Silence, - length, - sample_rate, - } - } - pub fn from_stream(file: &mut File, start_address: u64, end_address: u64) -> Self { - let mut length_buffer: [u8; 2] = [0; 2]; - file.read_exact(&mut length_buffer).unwrap(); - let length = u16::from_le_bytes(length_buffer) + 1; - - let mut frequency_divisor_buffer: [u8; 1] = [0]; - file.read_exact(&mut frequency_divisor_buffer).unwrap(); - let sample_rate: u32 = 1000000_u32 / (256_u32 - frequency_divisor_buffer[0] as u32); - - Self { - info: Some(BlockInfo { - block_type: BlockType::Silence, - start_address, - end_address, - }), - block_type: BlockType::Silence, - length, - sample_rate, - } - } -} - -impl Block for Silence { - fn to_bytes(&self) -> Vec { - let size_bytes: [u8; 4] = 3_u32.to_le_bytes(); - let length_bytes: [u8; 2] = (self.length - 1).to_le_bytes(); - let frequency_divisor: u8 = (256_u32 - 1000000_u32 / self.sample_rate) as u8; - - let result: Vec = vec![ - self.block_type as u8, - size_bytes[0], - size_bytes[1], - size_bytes[2], - length_bytes[0], - length_bytes[1], - frequency_divisor, - ]; - - result - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for Silence { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Silence") - .field("length", &self.length) - .field("sample rate", &self.sample_rate) - .finish() - } -} - -// Marker - -pub struct Marker { - info: Option, - block_type: BlockType, - value: u16, -} - -impl Marker { - pub fn new(value: u16) -> Self { - Self { - info: None, - block_type: BlockType::Marker, - value, - } - } - pub fn from_stream(file: &mut File, start_address: u64, end_address: u64) -> Self { - let mut value_buffer: [u8; 2] = [0; 2]; - file.read_exact(&mut value_buffer).unwrap(); - let value = u16::from_le_bytes(value_buffer); - - Self { - info: Some(BlockInfo { - block_type: BlockType::Marker, - start_address, - end_address, - }), - block_type: BlockType::Marker, - value, - } - } -} - -impl Block for Marker { - fn to_bytes(&self) -> Vec { - let size_bytes: [u8; 4] = 2_u32.to_le_bytes(); - let value_bytes: [u8; 2] = self.value.to_le_bytes(); - - let result: Vec = vec![ - self.block_type as u8, - size_bytes[0], - size_bytes[1], - size_bytes[2], - value_bytes[0], - value_bytes[1], - ]; - - result - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for Marker { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Marker") - .field("value", &self.value) - .finish() - } -} - -// Text - -pub struct Text { - pub info: Option, - pub block_type: BlockType, - pub data: Vec, -} - -impl Text { - pub fn new(data: Vec) -> Self { - Self { - info: None, - block_type: BlockType::Text, - data, - } - } - pub fn from_stream(file: &mut File, start_address: u64, end_address: u64) -> Self { - let address = file.stream_position().unwrap(); - let mut data: Vec = vec![0; (end_address - address) as usize]; - file.read_exact(&mut data).unwrap(); - - Self { - info: Some(BlockInfo { - block_type: BlockType::Text, - start_address, - end_address, - }), - block_type: BlockType::Text, - data, - } - } -} - -impl Block for Text { - fn to_bytes(&self) -> Vec { - let size_bytes: [u8; 4] = (self.data.len() as u32).to_le_bytes(); - - let mut result: Vec = vec![ - self.block_type as u8, - size_bytes[0], - size_bytes[1], - size_bytes[2], - ]; - - result.extend_from_slice(&self.data); - - result - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for Text { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Text") - .field("size", &self.data.len()) - .finish() - } -} - -// RepeatStart - -pub struct RepeatStart { - info: Option, - block_type: BlockType, - count: u16, -} - -impl RepeatStart { - pub fn new(count: u16) -> Self { - Self { - info: None, - block_type: BlockType::RepeatStart, - count, - } - } - pub fn from_stream(file: &mut File, start_address: u64, end_address: u64) -> Self { - let mut count_buffer: [u8; 2] = [0; 2]; - file.read_exact(&mut count_buffer).unwrap(); - let count = u16::from_le_bytes(count_buffer) + 1; - - Self { - info: Some(BlockInfo { - block_type: BlockType::RepeatStart, - start_address, - end_address, - }), - block_type: BlockType::RepeatStart, - count, - } - } -} - -impl Block for RepeatStart { - fn to_bytes(&self) -> Vec { - let size_bytes: [u8; 4] = 2_u32.to_le_bytes(); - let count_bytes: [u8; 2] = (self.count - 1).to_le_bytes(); - - let result: Vec = vec![ - self.block_type as u8, - size_bytes[0], - size_bytes[1], - size_bytes[2], - count_bytes[0], - count_bytes[1], - ]; - - result - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for RepeatStart { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RepeatStart") - .field("count", &self.count) - .finish() - } -} - -// RepeatEnd - -pub struct RepeatEnd { - info: Option, - block_type: BlockType, -} - -impl RepeatEnd { - #[cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] - pub fn new() -> Self { - Self { - info: None, - block_type: BlockType::RepeatEnd, - } - } - pub fn from_stream(_file: &mut File, start_address: u64, end_address: u64) -> Self { - Self { - info: Some(BlockInfo { - block_type: BlockType::RepeatEnd, - start_address, - end_address, - }), - block_type: BlockType::RepeatEnd, - } - } -} - -impl Block for RepeatEnd { - fn to_bytes(&self) -> Vec { - vec![self.block_type as u8] - } - fn get_info(&self) -> &Option { - &self.info - } -} - -impl fmt::Debug for RepeatEnd { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RepeatEnd").finish() - } -} diff --git a/src/voc/mod.rs b/src/voc/mod.rs deleted file mode 100644 index 0e54d41..0000000 --- a/src/voc/mod.rs +++ /dev/null @@ -1,211 +0,0 @@ -use std::fs::File; -use std::io::{Read, Seek, Write}; - -use crate::voc::blocks::*; - -pub mod blocks; - -#[derive(Debug)] -pub struct VocFile { - pub version: (u8, u8), - pub checksum: bool, - pub blocks: Vec, -} - -impl VocFile { - const SIGNATURE1: &'static str = "Creative Voice File"; - const SIGNATURE2: [u8; 3] = [0x1A, 0x1A, 0x0]; - - fn checksum(version: i16) -> [u8; 2] { - (!version + 0x1234).to_le_bytes() - } - - fn check_signatures(file: &mut File) { - // Signature - Creative Voice File - let mut signature1_buffer: [u8; 19] = [0; 19]; - file.read_exact(&mut signature1_buffer).unwrap(); - let signature1_str = std::str::from_utf8(&signature1_buffer).unwrap(); - assert_eq!( - signature1_str, - Self::SIGNATURE1, - "Bad file. Expected \"{}\" got \"{}\"", - Self::SIGNATURE1, - signature1_str - ); - - // Signature - 1A 1A 00 - let mut signature2_buffer: [u8; 3] = [0; 3]; - file.read_exact(&mut signature2_buffer).unwrap(); - assert_eq!( - signature2_buffer, - Self::SIGNATURE2, - "Bad file. Expected {:02X?} got {:02X?}", - Self::SIGNATURE2, - signature2_buffer - ); - } - - fn parse_version(file: &mut File) -> (u8, u8, i16) { - // Version - let mut version_buffer: [u8; 2] = [0; 2]; - file.read_exact(&mut version_buffer).unwrap(); - let version_int = i16::from_le_bytes(version_buffer); - (version_buffer[1], version_buffer[0], version_int) - } - - fn parse_checksum(file: &mut File, version: i16) -> bool { - // Version checksum - let mut checksum_buffer: [u8; 2] = [0; 2]; - file.read_exact(&mut checksum_buffer).unwrap(); - checksum_buffer == Self::checksum(version) - } - - fn parse_block_type(file: &mut File) -> BlockType { - // Block Type - let mut block_type_buffer: [u8; 1] = [0]; - file.read_exact(&mut block_type_buffer).unwrap(); - match block_type_buffer[0] { - 0 => BlockType::Terminator, - 1 => BlockType::SoundData, - 2 => BlockType::SoundDataContinuation, - 3 => BlockType::Silence, - 4 => BlockType::Marker, - 5 => BlockType::Text, - 6 => BlockType::RepeatStart, - 7 => BlockType::RepeatEnd, - _ => panic!("Bad block type. Got {}", block_type_buffer[0]), - } - } - - fn parse_block_size(file: &mut File, block_type: BlockType) -> u32 { - match block_type { - BlockType::Terminator | BlockType::RepeatEnd => 0, - _ => { - let mut block_size_buffer: [u8; 3] = [0; 3]; - file.read_exact(&mut block_size_buffer).unwrap(); - u32::from_le_bytes([ - block_size_buffer[0], - block_size_buffer[1], - block_size_buffer[2], - 0, - ]) - } - } - } - - fn create_block( - block_type: BlockType, - file: &mut File, - block_start_address: u64, - block_end_address: u64, - ) -> Box { - match block_type { - BlockType::Terminator => Box::new(Terminator::from_stream( - file, - block_start_address, - block_end_address, - )), - BlockType::SoundData => Box::new(SoundData::from_stream( - file, - block_start_address, - block_end_address, - )), - BlockType::SoundDataContinuation => Box::new(SoundDataContinuation::from_stream( - file, - block_start_address, - block_end_address, - )), - BlockType::Silence => Box::new(Silence::from_stream( - file, - block_start_address, - block_end_address, - )), - BlockType::Marker => Box::new(Marker::from_stream( - file, - block_start_address, - block_end_address, - )), - BlockType::Text => Box::new(Text::from_stream( - file, - block_start_address, - block_end_address, - )), - BlockType::RepeatStart => Box::new(RepeatStart::from_stream( - file, - block_start_address, - block_end_address, - )), - BlockType::RepeatEnd => Box::new(RepeatEnd::from_stream( - file, - block_start_address, - block_end_address, - )), - } - } - - pub fn from_file(file_name: &str) -> Self { - let mut file = File::open(file_name).unwrap(); - - Self::check_signatures(&mut file); - let version = Self::parse_version(&mut file); - - let mut voc_file = VocFile { - version: (version.0, version.1), - checksum: Self::parse_checksum(&mut file, version.2), - blocks: Vec::new(), - }; - - loop { - let block_start_address = file.stream_position().unwrap(); - let block_type = Self::parse_block_type(&mut file); - let block_size = Self::parse_block_size(&mut file, block_type); - - let current_address = file.stream_position().unwrap(); - let block_end_address = current_address + u64::from(block_size); - - let block = Self::create_block( - block_type, - &mut file, - block_start_address, - block_end_address, - ); - - voc_file.blocks.push(block); - - if block_type == BlockType::Terminator { - break; - } - } - voc_file - } - - pub fn to_bytes(&self) -> Vec { - let mut result: Vec = Vec::new(); - - // Signature1 - result.extend_from_slice(Self::SIGNATURE1.as_bytes()); - - // Signature2 - result.extend_from_slice(&Self::SIGNATURE2); - - // Version - result.extend_from_slice(&[self.version.1, self.version.0]); - - // Version Checksum - let version_int = i16::from_le_bytes([self.version.1, self.version.0]); - result.extend_from_slice(&Self::checksum(version_int)); - - // Iterate Blocks - for block in &self.blocks { - result.extend_from_slice(&block.to_bytes()); - } - - result - } - - pub fn to_file(&self, filename: &str) { - let mut fp = File::create(filename).unwrap(); - drop(fp.write(&self.to_bytes())); - drop(fp.flush()); - } -} diff --git a/tests/EDEN.MUS b/tests/EDEN.MUS deleted file mode 100644 index 5ca167b..0000000 Binary files a/tests/EDEN.MUS and /dev/null differ diff --git a/tests/unit_tests.rs b/tests/unit_tests.rs deleted file mode 100644 index 244d0d7..0000000 --- a/tests/unit_tests.rs +++ /dev/null @@ -1,24 +0,0 @@ -mod tests { - use voctool::voc::blocks::BlockType; - use voctool::voc::VocFile; - - #[test] - fn text_eden_mus() { - let tests_path = std::env::current_dir().unwrap().join("tests"); - let file_path = tests_path.join("EDEN.MUS"); - let voc = VocFile::from_file(file_path.to_str().unwrap()); - - // Version - assert_eq!((1, 10), voc.version); - - // Checksum - assert!(voc.checksum); - - // Text Block - let block = voc.blocks.get(0).unwrap().get_info().as_ref().unwrap(); - assert_eq!(BlockType::Text, block.block_type); - assert_eq!(0x1A, block.start_address); - assert_eq!(0xB5, block.end_address); - assert_eq!(155, block.get_size()); - } -}