use std::io::{Cursor, Write as _};
use bitvec::{array::BitArray, order::Lsb0, vec::BitVec, view::BitView as _};
use byteorder::{LittleEndian, WriteBytesExt as _};
use symphonia_core::{
errors::{Error, Result},
io::{MediaSourceStream, ReadBytes as _},
};
use crate::{
bits::{read, read_bool, read_write, read_write_bool, write},
chunk::Fmt,
codebook::CodebookLibrary,
};
pub(crate) struct IdentHeader([u8; 30]);
impl IdentHeader {
pub(crate) fn new(format: &Fmt) -> Result<Self> {
let mut bytes = Cursor::new([0; 30]);
bytes.write_u8(1)?;
bytes.write_all(b"vorbis")?;
bytes.write_u32::<LittleEndian>(0)?;
bytes.write_u8(
u8::try_from(format.channels.get())
.map_err(|_| Error::LimitError("too many channels"))?,
)?;
bytes.write_u32::<LittleEndian>(format.sample_rate.get())?;
bytes.write_i32::<LittleEndian>(0)?;
bytes.write_i32::<LittleEndian>(format.avg_bytes_per_second.cast_signed() * 8)?;
bytes.write_i32::<LittleEndian>(0)?;
bytes.write_u8(format.block_size_0 | (format.block_size_1 << 4))?;
bytes.write_u8(1)?;
Ok(Self(bytes.into_inner()))
}
pub(crate) const fn into_inner(self) -> [u8; 30] {
self.0
}
}
pub(crate) struct SetupHeader {
bytes: Vec<u8>,
pub(crate) mode_block_flags: BitArray<u64, Lsb0>,
pub(crate) mode_bits: u32,
}
impl SetupHeader {
#[expect(clippy::too_many_lines)]
pub(crate) fn new(fmt: &Fmt, mss: &mut MediaSourceStream) -> Result<Self> {
let mut bytes = BitVec::<_, Lsb0>::new();
bytes.write_u8(5)?;
bytes.write_all(b"vorbis")?;
let setup_start = fmt.setup_packet_offset + 2;
mss.ignore_bytes(u64::from(setup_start))?;
let buf =
mss.read_boxed_slice_exact((fmt.first_audio_packet_offset - setup_start) as usize)?;
let buf = buf.view_bits::<Lsb0>();
let (mut buf, codebook_count_minus_one): (_, u16) = read_write(buf, &mut bytes, 8);
let codebook_count = codebook_count_minus_one + 1;
let codebook_lib = CodebookLibrary::from_aotuv();
for _ in 0..codebook_count {
let id: u16;
(buf, id) = read(buf, 10);
let new_bytes = codebook_lib.rebuild(id as usize)?;
bytes.extend(new_bytes);
}
write(0_u8, &mut bytes, 6);
write(0_u16, &mut bytes, 16);
let (mut buf, floor_count_minus_one): (_, u8) = read_write(buf, &mut bytes, 6);
let floor_count = floor_count_minus_one + 1;
for _ in 0..floor_count {
write(1_u16, &mut bytes, 16);
let floor_partitions: usize;
(buf, floor_partitions) = read_write(buf, &mut bytes, 5);
let mut floor_partition_class_list = Vec::with_capacity(floor_partitions);
let mut maximum_class = 0;
for _ in 0..floor_partitions {
let floor_partition_class: u8;
(buf, floor_partition_class) = read_write(buf, &mut bytes, 4);
floor_partition_class_list.push(floor_partition_class);
maximum_class = maximum_class.max(floor_partition_class);
}
let floor_class_dimensions_list = (0..=maximum_class)
.map(|_| {
let class_dimensions_minus_one: u8;
(buf, class_dimensions_minus_one) = read_write(buf, &mut bytes, 3);
let class_subclasses: u8;
(buf, class_subclasses) = read_write(buf, &mut bytes, 2);
if class_subclasses != 0 {
let masterbook: u8;
(buf, masterbook) = read_write(buf, &mut bytes, 8);
if u16::from(masterbook) >= codebook_count {
return symphonia_core::errors::decode_error(
"wem: invalid floor 1 master book",
);
}
}
for _ in 0..(1 << u32::from(class_subclasses)) {
let subclass_book_plus_one: u8;
(buf, subclass_book_plus_one) = read_write(buf, &mut bytes, 8);
let subclass_book = i16::from(subclass_book_plus_one) - 1;
if subclass_book >= 0 && subclass_book >= codebook_count.cast_signed() {
return symphonia_core::errors::decode_error(
"wem: invalid floor 1 subclass book",
);
}
}
Ok(class_dimensions_minus_one + 1)
})
.collect::<Result<Vec<_>>>()?;
let _floor_multiplier_minus_one: u8;
(buf, _floor_multiplier_minus_one) = read_write(buf, &mut bytes, 2);
let range_bits: usize;
(buf, range_bits) = read_write(buf, &mut bytes, 4);
for current_class_number in floor_partition_class_list {
for _ in 0..floor_class_dimensions_list[current_class_number as usize] {
let _x: u16;
(buf, _x) = read_write(buf, &mut bytes, range_bits);
}
}
}
let (mut buf, residue_count_minus_one): (_, u8) = read_write(buf, &mut bytes, 6);
let residue_count = residue_count_minus_one + 1;
for _ in 0..residue_count {
let residue_type: u16;
(buf, residue_type) = read(buf, 2);
write(residue_type, &mut bytes, 16);
if residue_type > 2 {
return symphonia_core::errors::decode_error("wem: corrupt invalid residue type");
}
let _residue_begin: u32;
(buf, _residue_begin) = read_write(buf, &mut bytes, 24);
let _residue_end: u32;
(buf, _residue_end) = read_write(buf, &mut bytes, 24);
let _residue_partition_size_minus_one: u32;
(buf, _residue_partition_size_minus_one) = read_write(buf, &mut bytes, 24);
let residue_classifications_minus_one: u8;
(buf, residue_classifications_minus_one) = read_write(buf, &mut bytes, 6);
let residue_classifications = residue_classifications_minus_one + 1;
let residue_classbook: u8;
(buf, residue_classbook) = read_write(buf, &mut bytes, 8);
if u16::from(residue_classbook) >= codebook_count {
return symphonia_core::errors::decode_error("wem: corrupt residue classbook");
}
let residue_cascade = (0..residue_classifications)
.map(|_| {
let low_bits: u8;
(buf, low_bits) = read_write(buf, &mut bytes, 3);
let bit_flag;
(buf, bit_flag) = read_bool(buf);
bytes.push(bit_flag);
let high_bits = if bit_flag {
let high_bits: u8;
(buf, high_bits) = read_write(buf, &mut bytes, 5);
high_bits
} else {
0
};
u32::from(high_bits) * 8 + u32::from(low_bits)
})
.collect::<Vec<_>>();
residue_cascade
.into_iter()
.try_for_each(|residue_cascade| {
for offset in 0..8 {
if (residue_cascade & (1 << offset)) > 0 {
let residue_book: u8;
(buf, residue_book) = read_write(buf, &mut bytes, 8);
if u16::from(residue_book) >= codebook_count {
return symphonia_core::errors::decode_error(
"wem: corrupt residue book",
);
}
}
}
Ok(())
})?;
}
let (mut buf, mapping_count_minus_one): (_, u8) = read_write(buf, &mut bytes, 6);
let mapping_count = mapping_count_minus_one + 1;
for _ in 0..mapping_count {
write(0_u16, &mut bytes, 16);
let submaps_flag;
(buf, submaps_flag) = read_write_bool(buf, &mut bytes);
let submaps = if submaps_flag {
let submaps_minus_one: u8;
(buf, submaps_minus_one) = read_write(buf, &mut bytes, 4);
submaps_minus_one + 1
} else {
1
};
let square_polar_flag;
(buf, square_polar_flag) = read_write_bool(buf, &mut bytes);
if square_polar_flag {
let coupling_steps_minus_one: u16;
(buf, coupling_steps_minus_one) = read_write(buf, &mut bytes, 8);
let coupling_steps = coupling_steps_minus_one + 1;
for _ in 0..coupling_steps {
let magnitude: u32;
(buf, magnitude) = read_write(
buf,
&mut bytes,
crate::math::log2(u32::from(fmt.channels.get()) - 1) as usize,
);
let angle: u32;
(buf, angle) = read_write(
buf,
&mut bytes,
crate::math::log2(u32::from(fmt.channels.get()) - 1) as usize,
);
if angle == magnitude
|| magnitude >= u32::from(fmt.channels.get())
|| angle >= u32::from(fmt.channels.get())
{
return symphonia_core::errors::decode_error("wem: corrupt coupling");
}
}
}
let mapping_reserved: u8;
(buf, mapping_reserved) = read_write(buf, &mut bytes, 2);
if mapping_reserved != 0 {
return symphonia_core::errors::decode_error(
"wem: corrupt mapping reserved field nonzero",
);
}
if submaps > 1 {
for _ in 0..fmt.channels.get() {
let mapping_mux: u8;
(buf, mapping_mux) = read_write(buf, &mut bytes, 4);
if mapping_mux >= submaps {
return symphonia_core::errors::decode_error(
"wem: corrupt mapping mux >= submaps",
);
}
}
}
for _ in 0..submaps {
let _time_config: u8;
(buf, _time_config) = read_write(buf, &mut bytes, 8);
let floor_number: u8;
(buf, floor_number) = read_write(buf, &mut bytes, 8);
if floor_number >= floor_count {
return symphonia_core::errors::decode_error("wem: corrupt floor mapping");
}
let residue_number: u8;
(buf, residue_number) = read_write(buf, &mut bytes, 8);
if residue_number >= residue_count {
return symphonia_core::errors::decode_error("wem: corrupt residue mapping");
}
}
}
let (mut buf, mode_count_minus_one): (_, u8) = read_write(buf, &mut bytes, 6);
let mode_count = mode_count_minus_one + 1;
let mut mode_block_flags = BitArray::new(0);
for index in 0..mode_count {
let block_flag;
(buf, block_flag) = read_write_bool(buf, &mut bytes);
mode_block_flags.set(index as usize, block_flag);
write(0_u16, &mut bytes, 16);
write(0_u16, &mut bytes, 16);
let mapping: u8;
(buf, mapping) = read_write(buf, &mut bytes, 8);
if mapping >= mapping_count {
return symphonia_core::errors::decode_error("wem: corrupt invalid mode mapping");
}
}
let mode_bits = crate::math::log2(u32::from(mode_count_minus_one));
write(1_u8, &mut bytes, 1);
Ok(Self {
bytes: bytes.into_vec(),
mode_block_flags,
mode_bits,
})
}
pub(crate) fn into_inner(self) -> Vec<u8> {
self.bytes
}
}