use oxideav_core::bits::{BitReader, BitWriter};
use crate::ics_info::WindowSequence;
use crate::{Error, Result};
pub const ZERO_HCB: u8 = 0;
pub const FIRST_PAIR_HCB: u8 = 5;
pub const ESC_HCB: u8 = 11;
pub const NOISE_HCB: u8 = 13;
pub const INTENSITY_HCB2: u8 = 14;
pub const INTENSITY_HCB: u8 = 15;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Codebook {
Zero,
Quad {
number: u8,
unsigned: bool,
},
Pair {
number: u8,
unsigned: bool,
},
Esc,
Reserved12,
Noise,
IntensityOutOfPhase,
IntensityInPhase,
}
impl Codebook {
pub fn from_value(value: u8) -> Self {
match value & 0x0f {
0 => Codebook::Zero,
n @ 1..=4 => Codebook::Quad {
number: n,
unsigned: matches!(n, 3 | 4),
},
n @ 5..=10 => Codebook::Pair {
number: n,
unsigned: matches!(n, 7..=10),
},
11 => Codebook::Esc,
12 => Codebook::Reserved12,
13 => Codebook::Noise,
14 => Codebook::IntensityOutOfPhase,
15 => Codebook::IntensityInPhase,
_ => unreachable!("masked to 0..=15"),
}
}
pub fn is_intensity(self) -> bool {
matches!(
self,
Codebook::IntensityInPhase | Codebook::IntensityOutOfPhase
)
}
pub fn is_noise(self) -> bool {
matches!(self, Codebook::Noise)
}
pub fn is_zero(self) -> bool {
matches!(self, Codebook::Zero)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Section {
pub codebook: u8,
pub start: u8,
pub end: u8,
}
impl Section {
pub fn len(self) -> u8 {
self.end - self.start
}
pub fn is_empty(self) -> bool {
self.end == self.start
}
pub fn codebook_kind(self) -> Codebook {
Codebook::from_value(self.codebook)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SectionData {
pub sections: Vec<Vec<Section>>,
pub sfb_cb: Vec<Vec<u8>>,
}
impl SectionData {
pub fn parse(
reader: &mut BitReader<'_>,
window_sequence: WindowSequence,
num_window_groups: u8,
max_sfb: u8,
) -> Result<Self> {
let (sect_esc_val, len_bits) = if window_sequence.is_eight_short() {
((1u32 << 3) - 1, 3u32) } else {
((1u32 << 5) - 1, 5u32) };
let mut sections: Vec<Vec<Section>> = Vec::with_capacity(num_window_groups as usize);
let mut sfb_cb: Vec<Vec<u8>> = Vec::with_capacity(num_window_groups as usize);
for _g in 0..num_window_groups {
let mut group_sections: Vec<Section> = Vec::new();
let mut group_sfb_cb: Vec<u8> = vec![ZERO_HCB; max_sfb as usize];
let mut k: u32 = 0;
let max = max_sfb as u32;
while k < max {
let sect_cb = read_u8(reader, 4)?;
let mut sect_len: u32 = 0;
loop {
let incr = reader
.read_u32(len_bits)
.map_err(|_| Error::UnexpectedEnd)?;
if incr == sect_esc_val {
sect_len += sect_esc_val;
continue;
}
sect_len += incr;
break;
}
let start = k;
let end = k + sect_len;
if end > max {
return Err(Error::SectionDataOverrun);
}
for sfb in start..end {
group_sfb_cb[sfb as usize] = sect_cb;
}
group_sections.push(Section {
codebook: sect_cb,
start: start as u8,
end: end as u8,
});
k = end;
}
sections.push(group_sections);
sfb_cb.push(group_sfb_cb);
}
Ok(SectionData { sections, sfb_cb })
}
pub fn parse_er(
reader: &mut BitReader<'_>,
window_sequence: WindowSequence,
num_window_groups: u8,
max_sfb: u8,
) -> Result<Self> {
let (sect_esc_val, len_bits) = if window_sequence.is_eight_short() {
((1u32 << 3) - 1, 3u32)
} else {
((1u32 << 5) - 1, 5u32)
};
let mut sections: Vec<Vec<Section>> = Vec::with_capacity(num_window_groups as usize);
let mut sfb_cb: Vec<Vec<u8>> = Vec::with_capacity(num_window_groups as usize);
for _g in 0..num_window_groups {
let mut group_sections: Vec<Section> = Vec::new();
let mut group_sfb_cb: Vec<u8> = vec![ZERO_HCB; max_sfb as usize];
let mut k: u32 = 0;
let max = max_sfb as u32;
while k < max {
let sect_cb = read_u8(reader, 5)?;
let mut sect_len: u32 = 0;
if er_uses_escape_coding(sect_cb) {
loop {
let incr = reader
.read_u32(len_bits)
.map_err(|_| Error::UnexpectedEnd)?;
if incr == sect_esc_val {
sect_len += sect_esc_val;
continue;
}
sect_len += incr;
break;
}
} else {
sect_len = 1;
}
let start = k;
let end = k + sect_len;
if end > max {
return Err(Error::SectionDataOverrun);
}
for sfb in start..end {
group_sfb_cb[sfb as usize] = sect_cb;
}
group_sections.push(Section {
codebook: sect_cb,
start: start as u8,
end: end as u8,
});
k = end;
}
sections.push(group_sections);
sfb_cb.push(group_sfb_cb);
}
Ok(SectionData { sections, sfb_cb })
}
pub fn write_er(
&self,
writer: &mut BitWriter,
window_sequence: WindowSequence,
max_sfb: u8,
) -> Result<()> {
let (sect_esc_val, len_bits) = if window_sequence.is_eight_short() {
(7u32, 3u32)
} else {
(31u32, 5u32)
};
for group_sections in &self.sections {
if group_sections.is_empty() {
if max_sfb != 0 {
return Err(Error::SectionDataEncodeInvalid);
}
continue;
}
if group_sections[0].start != 0 {
return Err(Error::SectionDataEncodeInvalid);
}
for w in group_sections.windows(2) {
if w[0].end != w[1].start {
return Err(Error::SectionDataEncodeInvalid);
}
}
if group_sections.last().unwrap().end != max_sfb {
return Err(Error::SectionDataEncodeInvalid);
}
for section in group_sections {
if section.codebook > 0x1f {
return Err(Error::SectionDataEncodeInvalid);
}
let sect_len = section.len() as u32;
if sect_len == 0 {
return Err(Error::SectionDataEncodeInvalid);
}
writer.write_u32(section.codebook as u32, 5);
if er_uses_escape_coding(section.codebook) {
let mut remaining = sect_len;
while remaining >= sect_esc_val {
writer.write_u32(sect_esc_val, len_bits);
remaining -= sect_esc_val;
}
writer.write_u32(remaining, len_bits);
} else {
if sect_len != 1 {
return Err(Error::SectionDataEncodeInvalid);
}
}
}
}
Ok(())
}
pub fn num_sec(&self, group: usize) -> usize {
self.sections.get(group).map_or(0, Vec::len)
}
pub fn write(
&self,
writer: &mut BitWriter,
window_sequence: WindowSequence,
max_sfb: u8,
) -> Result<()> {
let (sect_esc_val, len_bits) = if window_sequence.is_eight_short() {
(7u32, 3u32) } else {
(31u32, 5u32) };
for group_sections in &self.sections {
if group_sections.is_empty() {
if max_sfb != 0 {
return Err(Error::SectionDataEncodeInvalid);
}
continue;
}
if group_sections[0].start != 0 {
return Err(Error::SectionDataEncodeInvalid);
}
for w in group_sections.windows(2) {
if w[0].end != w[1].start {
return Err(Error::SectionDataEncodeInvalid);
}
}
if group_sections.last().unwrap().end != max_sfb {
return Err(Error::SectionDataEncodeInvalid);
}
for section in group_sections {
if section.codebook > 0x0f {
return Err(Error::SectionDataEncodeInvalid);
}
let sect_len = section.len() as u32;
if sect_len == 0 {
return Err(Error::SectionDataEncodeInvalid);
}
writer.write_u32(section.codebook as u32, 4);
let mut remaining = sect_len;
while remaining >= sect_esc_val {
writer.write_u32(sect_esc_val, len_bits);
remaining -= sect_esc_val;
}
writer.write_u32(remaining, len_bits);
}
}
Ok(())
}
}
fn read_u8(reader: &mut BitReader<'_>, n: u32) -> Result<u8> {
debug_assert!(n <= 8);
Ok(reader.read_u32(n).map_err(|_| Error::UnexpectedEnd)? as u8)
}
fn er_uses_escape_coding(sect_cb: u8) -> bool {
sect_cb < 11 || (sect_cb > 11 && sect_cb < 16)
}