* VocFile

- Dont panic on invalid version checksum
- Add new field to store checksum status

* BlockInfo
- New struct
- Contains start and end address of the block in the file
- Contains a get_size() function

* Block Trait
- Add a get_info() function that returns a BlockInfo

* Main
- Prettify the *info* command.
- Now shows the file version, checksum, and info about all the blocks.
This commit is contained in:
Pedro de Oliveira 2022-11-14 20:51:24 +00:00
parent 1dd3997587
commit 59cbcff192
2 changed files with 155 additions and 76 deletions

View File

@ -7,25 +7,54 @@ fn main() {
.subcommand_value_name("COMMAND") .subcommand_value_name("COMMAND")
.subcommand_help_heading("COMMAND") .subcommand_help_heading("COMMAND")
.subcommand(clap::Command::new("info") .subcommand(clap::Command::new("info")
.about("Show information about the <file>") .about("Show information about the <file>")
.arg( .arg(
clap::arg!(<file>) clap::arg!(<file>)
.value_parser(clap::value_parser!(String)) .value_parser(clap::value_parser!(String))
.required(true) .required(true)
.help("Filename"), .help("Filename"),
), ),
)
.subcommand(clap::Command::new("extract")
.about("Extract all the blocks from the <file>")
.arg(
clap::arg!(<file>)
.value_parser(clap::value_parser!(String))
.required(true)
.help("Filename"),
),
); );
let matches = cmd.get_matches(); let matches = cmd.get_matches();
match matches.subcommand() { match matches.subcommand() {
Some(("info", arguments)) => { Some(("info", arguments)) => {
let filename = arguments.get_one::<String>("file").unwrap();
let voc = voc::VocFile::from_file(filename);
println!("Version {}.{} - Checksum: {}",
voc.version.0,
voc.version.1,
if voc.checksum { "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);
}
}
}
Some(("extract", _arguments)) => {
/*
let filename = arguments.get_one::<String>("file").unwrap(); let filename = arguments.get_one::<String>("file").unwrap();
let voc = voc::VocFile::from_file(filename); let voc = voc::VocFile::from_file(filename);
println!("{:?}", voc); println!("{:?}", voc);
voc.to_file("lulz.voc"); voc.to_file("lulz.voc");
*/
} }
_ => unreachable!("parser should ensure only valid subcommand names are used"), _ => unreachable!("parser should ensure only valid subcommand names are used"),
}; };
} }

View File

@ -31,6 +31,7 @@ pub enum BlockType {
#[derive(Debug)] #[derive(Debug)]
pub struct VocFile { pub struct VocFile {
pub version: (u8, u8), pub version: (u8, u8),
pub checksum: bool,
pub blocks: Vec<BlockT>, pub blocks: Vec<BlockT>,
} }
@ -65,13 +66,13 @@ impl VocFile {
let mut checksum_buffer: [u8; 2] = [0; 2]; let mut checksum_buffer: [u8; 2] = [0; 2];
drop(fp.read(&mut checksum_buffer)); drop(fp.read(&mut checksum_buffer));
let checksum = (!version_int + 0x1234).to_le_bytes(); let checksum = (!version_int + 0x1234).to_le_bytes();
if checksum_buffer != checksum { let checksum_result = checksum_buffer == checksum;
panic!("Bad file. Expected {:02X?} got {:02X?}", checksum, checksum_buffer);
}
let mut voc = VocFile { version, blocks: Vec::new() }; let mut voc = VocFile { version, checksum: checksum_result, blocks: Vec::new() };
loop { loop {
let block_start_address = fp.seek(SeekFrom::Current(0)).unwrap() as u32;
// Block Type // Block Type
let mut block_type_buffer: [u8; 1] = [0]; let mut block_type_buffer: [u8; 1] = [0];
drop(fp.read(&mut block_type_buffer)); drop(fp.read(&mut block_type_buffer));
@ -98,41 +99,41 @@ impl VocFile {
); );
} }
let address = fp.seek(SeekFrom::Current(0)).unwrap() as u32; let current_address = fp.seek(SeekFrom::Current(0)).unwrap() as u32;
let end_address = address + block_size; let block_end_address = current_address + block_size;
match block_type { match block_type {
BlockType::Terminator => { BlockType::Terminator => {
let block = Terminator::from_stream(&mut fp, end_address); let block = Terminator::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
break; break;
} }
BlockType::SoundData => { BlockType::SoundData => {
let block = SoundData::from_stream(&mut fp, end_address); let block = SoundData::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
} }
BlockType::SoundDataContinuation => { BlockType::SoundDataContinuation => {
let block = SoundDataContinuation::from_stream(&mut fp, end_address); let block = SoundDataContinuation::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
} }
BlockType::Silence => { BlockType::Silence => {
let block = Silence::from_stream(&mut fp, end_address); let block = Silence::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
} }
BlockType::Marker => { BlockType::Marker => {
let block = Marker::from_stream(&mut fp, end_address); let block = Marker::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
} }
BlockType::Text => { BlockType::Text => {
let block = Text::from_stream(&mut fp, end_address); let block = Text::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
} }
BlockType::RepeatStart => { BlockType::RepeatStart => {
let block = RepeatStart::from_stream(&mut fp, end_address); let block = RepeatStart::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
} }
BlockType::RepeatEnd => { BlockType::RepeatEnd => {
let block = RepeatEnd::from_stream(&mut fp, end_address); let block = RepeatEnd::from_stream(&mut fp, block_start_address, block_end_address);
voc.blocks.push(Box::new(block)); voc.blocks.push(Box::new(block));
} }
} }
@ -177,28 +178,41 @@ type BlockT = Box<dyn Block>;
pub trait Block: fmt::Debug { pub trait Block: fmt::Debug {
fn to_bytes(&self) -> Vec<u8>; fn to_bytes(&self) -> Vec<u8>;
fn get_info(&self) -> &Option<BlockInfo>;
}
pub struct BlockInfo {
pub start_address: u32,
pub end_address: u32,
}
impl BlockInfo {
pub fn get_size(&self) -> u32 {
self.end_address - self.start_address
}
} }
// Terminator // Terminator
pub struct Terminator { pub struct Terminator {
info: Option<BlockInfo>,
block_type: BlockType, block_type: BlockType,
} }
impl Terminator { impl Terminator {
pub fn new() -> Self { pub fn new() -> Self { Self { info: None, block_type: BlockType::Terminator } }
Self { block_type: BlockType::Terminator } pub fn from_stream(_fp: &mut File, start_address: u32, end_address: u32) -> Self
} {
Self {
pub fn from_stream(_fp: &mut File, _end_address: u32) -> Self { info: Some(BlockInfo { start_address, end_address }),
Self::new() block_type: BlockType::Terminator,
}
} }
} }
impl Block for Terminator { impl Block for Terminator {
fn to_bytes(&self) -> Vec<u8> { fn to_bytes(&self) -> Vec<u8> { vec![self.block_type as u8] }
vec![self.block_type as u8] fn get_info(&self) -> &Option<BlockInfo> { &self.info }
}
} }
impl fmt::Debug for Terminator { impl fmt::Debug for Terminator {
@ -211,15 +225,17 @@ impl fmt::Debug for Terminator {
// SoundData // SoundData
pub struct SoundData { pub struct SoundData {
info: Option<BlockInfo>,
block_type: BlockType, block_type: BlockType,
pub sample_rate: u32, sample_rate: u32,
pub codec: Codec, codec: Codec,
pub data: Vec<u8>, data: Vec<u8>,
} }
impl SoundData { impl SoundData {
pub fn new(sample_rate: u32, codec: Codec, data: Vec<u8>) -> Self { pub fn new(sample_rate: u32, codec: Codec, data: Vec<u8>) -> Self {
Self { Self {
info: None,
block_type: BlockType::SoundData, block_type: BlockType::SoundData,
sample_rate, sample_rate,
codec, codec,
@ -227,7 +243,7 @@ impl SoundData {
} }
} }
pub fn from_stream(fp: &mut File, end_address: u32) -> Self { pub fn from_stream(fp: &mut File, start_address: u32, end_address: u32) -> Self {
let mut frequency_divisor_buffer: [u8; 1] = [0]; let mut frequency_divisor_buffer: [u8; 1] = [0];
drop(fp.read(&mut frequency_divisor_buffer)); drop(fp.read(&mut frequency_divisor_buffer));
let sample_rate: u32 = 1000000u32 / (256u32 - frequency_divisor_buffer[0] as u32); let sample_rate: u32 = 1000000u32 / (256u32 - frequency_divisor_buffer[0] as u32);
@ -250,7 +266,13 @@ impl SoundData {
let mut data: Vec<u8> = vec![0; (end_address - address) as usize]; let mut data: Vec<u8> = vec![0; (end_address - address) as usize];
drop(fp.read(&mut data)); drop(fp.read(&mut data));
Self::new(sample_rate, codec, data) Self {
info: Some(BlockInfo { start_address, end_address }),
block_type: BlockType::SoundData,
sample_rate,
codec,
data,
}
} }
} }
@ -272,6 +294,7 @@ impl Block for SoundData {
result result
} }
fn get_info(&self) -> &Option<BlockInfo> { &self.info }
} }
impl fmt::Debug for SoundData { impl fmt::Debug for SoundData {
@ -287,24 +310,29 @@ impl fmt::Debug for SoundData {
// SoundDataContinuation // SoundDataContinuation
pub struct SoundDataContinuation { pub struct SoundDataContinuation {
pub block_type: BlockType, info: Option<BlockInfo>,
pub data: Vec<u8>, block_type: BlockType,
data: Vec<u8>,
} }
impl SoundDataContinuation { impl SoundDataContinuation {
pub fn new(data: Vec<u8>) -> Self { pub fn new(data: Vec<u8>) -> Self {
Self { Self {
info: None,
block_type: BlockType::SoundDataContinuation, block_type: BlockType::SoundDataContinuation,
data, data,
} }
} }
pub fn from_stream(fp: &mut File, start_address: u32, end_address: u32) -> Self {
pub fn from_stream(fp: &mut File, end_address: u32) -> Self {
let address = fp.seek(SeekFrom::Current(0)).unwrap() as u32; let address = fp.seek(SeekFrom::Current(0)).unwrap() as u32;
let mut data: Vec<u8> = vec![0; (end_address - address) as usize]; let mut data: Vec<u8> = vec![0; (end_address - address) as usize];
drop(fp.read(&mut data)); drop(fp.read(&mut data));
Self::new(data) Self {
info: Some(BlockInfo { start_address, end_address }),
block_type: BlockType::SoundDataContinuation,
data,
}
} }
} }
@ -315,12 +343,13 @@ impl Block for SoundDataContinuation {
self.block_type as u8, self.block_type as u8,
size_bytes[0], size_bytes[0],
size_bytes[1], size_bytes[1],
size_bytes[2] size_bytes[2],
]; ];
result.extend_from_slice(&self.data); result.extend_from_slice(&self.data);
result result
} }
fn get_info(&self) -> &Option<BlockInfo> { &self.info }
} }
impl fmt::Debug for SoundDataContinuation { impl fmt::Debug for SoundDataContinuation {
@ -334,21 +363,22 @@ impl fmt::Debug for SoundDataContinuation {
// Silence // Silence
pub struct Silence { pub struct Silence {
pub block_type: BlockType, info: Option<BlockInfo>,
pub length: u16, block_type: BlockType,
pub sample_rate: u32, length: u16,
sample_rate: u32,
} }
impl Silence { impl Silence {
pub fn new(length: u16, sample_rate: u32) -> Self { pub fn new(length: u16, sample_rate: u32) -> Self {
Self { Self {
info: None,
block_type: BlockType::Silence, block_type: BlockType::Silence,
length, length,
sample_rate sample_rate,
} }
} }
pub fn from_stream(fp: &mut File, start_address: u32, end_address: u32) -> Self {
pub fn from_stream(fp: &mut File, _end_address: u32) -> Self {
let mut length_buffer: [u8; 2] = [0; 2]; let mut length_buffer: [u8; 2] = [0; 2];
drop(fp.read(&mut length_buffer)); drop(fp.read(&mut length_buffer));
let length = u16::from_le_bytes(length_buffer) + 1; let length = u16::from_le_bytes(length_buffer) + 1;
@ -357,7 +387,12 @@ impl Silence {
drop(fp.read(&mut frequency_divisor_buffer)); drop(fp.read(&mut frequency_divisor_buffer));
let sample_rate: u32 = 1000000u32 / (256u32 - frequency_divisor_buffer[0] as u32); let sample_rate: u32 = 1000000u32 / (256u32 - frequency_divisor_buffer[0] as u32);
Self::new(length, sample_rate) Self {
info: Some(BlockInfo { start_address, end_address }),
block_type: BlockType::Silence,
length,
sample_rate,
}
} }
} }
@ -379,6 +414,7 @@ impl Block for Silence {
result result
} }
fn get_info(&self) -> &Option<BlockInfo> { &self.info }
} }
impl fmt::Debug for Silence { impl fmt::Debug for Silence {
@ -393,6 +429,7 @@ impl fmt::Debug for Silence {
// Marker // Marker
pub struct Marker { pub struct Marker {
info: Option<BlockInfo>,
block_type: BlockType, block_type: BlockType,
value: u16, value: u16,
} }
@ -400,17 +437,21 @@ pub struct Marker {
impl Marker { impl Marker {
pub fn new(value: u16) -> Self { pub fn new(value: u16) -> Self {
Self { Self {
info: None,
block_type: BlockType::Marker, block_type: BlockType::Marker,
value value,
} }
} }
pub fn from_stream(fp: &mut File, start_address: u32, end_address: u32) -> Self {
pub fn from_stream(fp: &mut File, _end_address: u32) -> Self {
let mut value_buffer: [u8; 2] = [0; 2]; let mut value_buffer: [u8; 2] = [0; 2];
drop(fp.read(&mut value_buffer)); drop(fp.read(&mut value_buffer));
let value = u16::from_le_bytes(value_buffer); let value = u16::from_le_bytes(value_buffer);
Self::new(value) Self {
info: Some(BlockInfo { start_address, end_address }),
block_type: BlockType::Marker,
value,
}
} }
} }
@ -430,6 +471,7 @@ impl Block for Marker {
result result
} }
fn get_info(&self) -> &Option<BlockInfo> { &self.info }
} }
impl fmt::Debug for Marker { impl fmt::Debug for Marker {
@ -443,24 +485,23 @@ impl fmt::Debug for Marker {
// Text // Text
pub struct Text { pub struct Text {
block_type: BlockType, pub info: Option<BlockInfo>,
pub block_type: BlockType,
pub data: Vec<u8>, pub data: Vec<u8>,
} }
impl Text { impl Text {
pub fn new(data: Vec<u8>) -> Self { pub fn new(data: Vec<u8>) -> Self { Self { info: None, block_type: BlockType::Text, data } }
Self { pub fn from_stream(fp: &mut File, start_address: u32, end_address: u32) -> Self {
block_type: BlockType::Text,
data,
}
}
pub fn from_stream(fp: &mut File, end_address: u32) -> Self {
let address = fp.seek(SeekFrom::Current(0)).unwrap() as u32; let address = fp.seek(SeekFrom::Current(0)).unwrap() as u32;
let mut data: Vec<u8> = vec![0; (end_address - address) as usize]; let mut data: Vec<u8> = vec![0; (end_address - address) as usize];
drop(fp.read(&mut data)); drop(fp.read(&mut data));
Self::new(data) Self {
info: Some(BlockInfo { start_address, end_address }),
block_type: BlockType::Text,
data,
}
} }
} }
@ -479,6 +520,7 @@ impl Block for Text {
result result
} }
fn get_info(&self) -> &Option<BlockInfo> { &self.info }
} }
impl fmt::Debug for Text { impl fmt::Debug for Text {
@ -492,24 +534,29 @@ impl fmt::Debug for Text {
// RepeatStart // RepeatStart
pub struct RepeatStart { pub struct RepeatStart {
info: Option<BlockInfo>,
block_type: BlockType, block_type: BlockType,
pub count: u16 count: u16,
} }
impl RepeatStart { impl RepeatStart {
pub fn new(count: u16) -> Self { pub fn new(count: u16) -> Self {
Self { Self {
info: None,
block_type: BlockType::RepeatStart, block_type: BlockType::RepeatStart,
count count,
} }
} }
pub fn from_stream(fp: &mut File, start_address: u32, end_address: u32) -> Self {
pub fn from_stream(fp: &mut File, _end_address: u32) -> Self {
let mut count_buffer: [u8; 2] = [0; 2]; let mut count_buffer: [u8; 2] = [0; 2];
drop(fp.read(&mut count_buffer)); drop(fp.read(&mut count_buffer));
let count = u16::from_le_bytes(count_buffer) + 1; let count = u16::from_le_bytes(count_buffer) + 1;
Self::new(count) Self {
info: Some(BlockInfo { start_address, end_address }),
block_type: BlockType::RepeatStart,
count,
}
} }
} }
@ -529,6 +576,7 @@ impl Block for RepeatStart {
result result
} }
fn get_info(&self) -> &Option<BlockInfo> { &self.info }
} }
impl fmt::Debug for RepeatStart { impl fmt::Debug for RepeatStart {
@ -542,16 +590,17 @@ impl fmt::Debug for RepeatStart {
// RepeatEnd // RepeatEnd
pub struct RepeatEnd { pub struct RepeatEnd {
block_type: BlockType info: Option<BlockInfo>,
block_type: BlockType,
} }
impl RepeatEnd { impl RepeatEnd {
pub fn new() -> Self { pub fn new() -> Self { Self { info: None, block_type: BlockType::RepeatEnd } }
Self { block_type: BlockType::RepeatEnd } pub fn from_stream(_fp: &mut File, start_address: u32, end_address: u32) -> Self {
} Self {
info: Some(BlockInfo { start_address, end_address }),
pub fn from_stream(_fp: &mut File, _end_address: u32) -> Self { block_type: BlockType::RepeatEnd,
Self::new() }
} }
} }
@ -559,6 +608,7 @@ impl Block for RepeatEnd {
fn to_bytes(&self) -> Vec<u8> { fn to_bytes(&self) -> Vec<u8> {
vec![self.block_type as u8] vec![self.block_type as u8]
} }
fn get_info(&self) -> &Option<BlockInfo> { &self.info }
} }
impl fmt::Debug for RepeatEnd { impl fmt::Debug for RepeatEnd {