mod envelope;
mod error;
mod unit;
mod wave;
pub use self::envelope::*;
pub use self::error::*;
pub use self::unit::*;
pub use self::wave::*;
use crate::data::{FromRead, FromReadVar, WriteTo, WriteVarTo};
use std::io::SeekFrom;
use std::io::{Read, Seek, Write};
type PtvSignature = [u8; 8];
#[derive(Clone, Debug, PartialEq)]
pub struct Ptvoice {
pub legacy_basic_key: i32,
pub units: Box<[PtvUnit]>,
}
impl Ptvoice {
const SIGNATURE: PtvSignature = *b"PTVOICE-";
#[allow(clippy::inconsistent_digit_grouping)]
const VERSION: i32 = 2006_01_11;
pub fn new(units: Box<[PtvUnit]>) -> Self {
Self {
legacy_basic_key: 0,
units,
}
}
}
impl FromRead<Self> for Ptvoice {
type Error = PtvError;
fn from_read<R: Read>(source: &mut R) -> Result<Self, Self::Error> {
if Self::SIGNATURE != PtvSignature::from_read(source)? {
return Err(PtvError::Invalid);
}
if Self::VERSION < i32::from_read(source)? {
return Err(PtvError::Unsupported);
}
let _data_len = i32::from_read(source)?;
let legacy_basic_key = i32::from_read_var(source)?;
for _ in 0..2 {
if i32::from_read_var(source)? != 0 {
return Err(PtvError::Invalid)?;
}
}
let unit_count: usize = i32::from_read_var(source)?
.try_into()
.map_err(|_| PtvError::Invalid)?;
let units = (0..unit_count)
.map(|_| PtvUnit::from_read(source))
.collect::<Result<Box<[_]>, _>>()?;
Ok(Self {
legacy_basic_key,
units,
})
}
}
impl WriteTo for Ptvoice {
type Error = PtvError;
fn write_to<W: Write + Seek>(&self, sink: &mut W) -> Result<u64, Self::Error> {
let start_pos = Self::SIGNATURE.write_to(sink)?;
Self::VERSION.write_to(sink)?;
let data_len_pos = 0_i32.write_to(sink)?;
let data_start = self.legacy_basic_key.write_var_to(sink)?;
0_i32.write_var_to(sink)?;
0_i32.write_var_to(sink)?;
i32::try_from(self.units.len())
.map_err(|_| PtvError::Oversized)?
.write_var_to(sink)?;
for unit in self.units.iter() {
unit.write_to(sink)?;
}
let data_end = sink.stream_position()?;
let data_len = i32::try_from(data_end - data_start).map_err(|_| PtvError::Oversized)?;
sink.seek(SeekFrom::Start(data_len_pos))?;
data_len.write_to(sink)?;
sink.seek(SeekFrom::Start(data_end))?;
Ok(start_pos)
}
}
impl Default for Ptvoice {
fn default() -> Self {
Self::new(Box::new([]))
}
}