use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
use crate::toc::variable_bits;
pub const MAX_EMDF_PAYLOADS: usize = 64;
pub const MAX_EMDF_PAYLOAD_BYTES: usize = 65_536;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct EmdfPayloadConfig {
pub b_smpoffst: bool,
pub smpoffst: Option<u32>,
pub b_duration: bool,
pub duration: Option<u32>,
pub b_groupid: bool,
pub groupid: Option<u32>,
pub b_codecdata: bool,
pub codecdata: Option<u8>,
pub b_discard_unknown_payload: bool,
pub b_payload_frame_aligned: Option<bool>,
pub b_create_duplicate: Option<bool>,
pub b_remove_duplicate: Option<bool>,
pub priority: Option<u8>,
pub proc_allowed: Option<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmdfPayload {
pub emdf_payload_id: u32,
pub config: EmdfPayloadConfig,
pub payload_bytes: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EmdfPayloadsSubstream {
pub payloads: Vec<EmdfPayload>,
}
pub fn parse_emdf_payloads_substream(br: &mut BitReader<'_>) -> Result<EmdfPayloadsSubstream> {
let mut payloads = Vec::new();
loop {
if payloads.len() >= MAX_EMDF_PAYLOADS {
return Err(Error::invalid(
"ac4: emdf_payloads_substream() exceeded MAX_EMDF_PAYLOADS — malformed?",
));
}
let id_base = br.read_u32(5)?;
if id_base == 0 {
break;
}
let emdf_payload_id = if id_base == 31 {
31u32
.checked_add(variable_bits(br, 5)?)
.ok_or_else(|| Error::invalid("ac4: emdf_payload_id extension overflow"))?
} else {
id_base
};
let config = parse_emdf_payload_config(br)?;
let emdf_payload_size = variable_bits(br, 8)? as usize;
if emdf_payload_size > MAX_EMDF_PAYLOAD_BYTES {
return Err(Error::invalid(
"ac4: emdf_payload_size exceeded MAX_EMDF_PAYLOAD_BYTES — malformed?",
));
}
let mut payload_bytes = Vec::with_capacity(emdf_payload_size);
for _ in 0..emdf_payload_size {
payload_bytes.push(br.read_u32(8)? as u8);
}
payloads.push(EmdfPayload {
emdf_payload_id,
config,
payload_bytes,
});
}
byte_align(br)?;
Ok(EmdfPayloadsSubstream { payloads })
}
pub fn parse_emdf_payload_config(br: &mut BitReader<'_>) -> Result<EmdfPayloadConfig> {
let b_smpoffst = br.read_bit()?;
let smpoffst = if b_smpoffst {
Some(variable_bits(br, 11)?)
} else {
None
};
let b_duration = br.read_bit()?;
let duration = if b_duration {
Some(variable_bits(br, 11)?)
} else {
None
};
let b_groupid = br.read_bit()?;
let groupid = if b_groupid {
Some(variable_bits(br, 2)?)
} else {
None
};
let b_codecdata = br.read_bit()?;
let codecdata = if b_codecdata {
Some(br.read_u32(8)? as u8)
} else {
None
};
let b_discard_unknown_payload = br.read_bit()?;
let mut b_payload_frame_aligned = None;
let mut b_create_duplicate = None;
let mut b_remove_duplicate = None;
let mut priority = None;
let mut proc_allowed = None;
if !b_discard_unknown_payload {
if !b_smpoffst {
let aligned = br.read_bit()?;
b_payload_frame_aligned = Some(aligned);
if aligned {
b_create_duplicate = Some(br.read_bit()?);
b_remove_duplicate = Some(br.read_bit()?);
}
}
let aligned_or_offset = b_smpoffst || b_payload_frame_aligned.unwrap_or(false);
if aligned_or_offset {
priority = Some(br.read_u32(5)? as u8);
proc_allowed = Some(br.read_u32(2)? as u8);
}
}
Ok(EmdfPayloadConfig {
b_smpoffst,
smpoffst,
b_duration,
duration,
b_groupid,
groupid,
b_codecdata,
codecdata,
b_discard_unknown_payload,
b_payload_frame_aligned,
b_create_duplicate,
b_remove_duplicate,
priority,
proc_allowed,
})
}
fn byte_align(br: &mut BitReader<'_>) -> Result<()> {
let pos = br.bit_position();
let extra = (8 - (pos & 7)) & 7;
if extra > 0 {
br.skip(extra as u32)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
fn write_variable_bits(bw: &mut BitWriter, n_bits: u32, mut value: u32) {
let chunk_max = 1u32 << n_bits;
let mut chunks: Vec<u32> = Vec::new();
loop {
if value < chunk_max {
chunks.push(value);
break;
}
value -= chunk_max;
chunks.push(value & (chunk_max - 1));
value >>= n_bits;
}
for (i, c) in chunks.iter().rev().enumerate() {
bw.write_u32(*c, n_bits);
let more = i + 1 < chunks.len();
bw.write_bit(more);
}
}
#[test]
fn variable_bits_round_trip_small_values() {
for v in [0u32, 1, 2, 7, 8, 15, 16, 17, 31, 32, 100, 255, 256, 1023] {
let mut bw = BitWriter::new();
write_variable_bits(&mut bw, 2, v);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
assert_eq!(variable_bits(&mut br, 2).unwrap(), v, "v={v}");
}
}
#[test]
fn empty_substream_is_just_terminator() {
let mut bw = BitWriter::new();
bw.write_u32(0, 5);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_emdf_payloads_substream(&mut br).unwrap();
assert!(parsed.payloads.is_empty());
}
fn write_minimal_config(bw: &mut BitWriter) {
bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(true); }
#[test]
fn one_payload_with_minimal_config_round_trips() {
let mut bw = BitWriter::new();
bw.write_u32(7, 5);
write_minimal_config(&mut bw);
write_variable_bits(&mut bw, 8, 3);
bw.write_u32(0xAA, 8);
bw.write_u32(0xBB, 8);
bw.write_u32(0xCC, 8);
bw.write_u32(0, 5);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_emdf_payloads_substream(&mut br).unwrap();
assert_eq!(parsed.payloads.len(), 1);
let p = &parsed.payloads[0];
assert_eq!(p.emdf_payload_id, 7);
assert_eq!(p.payload_bytes, vec![0xAA, 0xBB, 0xCC]);
assert!(p.config.b_discard_unknown_payload);
assert!(p.config.priority.is_none());
assert!(p.config.proc_allowed.is_none());
}
#[test]
fn extended_payload_id_via_variable_bits_5() {
let mut bw = BitWriter::new();
bw.write_u32(31, 5);
write_variable_bits(&mut bw, 5, 9);
write_minimal_config(&mut bw);
write_variable_bits(&mut bw, 8, 0); bw.write_u32(0, 5); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_emdf_payloads_substream(&mut br).unwrap();
assert_eq!(parsed.payloads.len(), 1);
assert_eq!(parsed.payloads[0].emdf_payload_id, 40);
assert!(parsed.payloads[0].payload_bytes.is_empty());
}
#[test]
fn config_with_all_optional_fields_round_trips() {
let mut bw = BitWriter::new();
bw.write_u32(1, 5); bw.write_bit(true); write_variable_bits(&mut bw, 11, 5); bw.write_bit(true); write_variable_bits(&mut bw, 11, 12); bw.write_bit(true); write_variable_bits(&mut bw, 2, 2); bw.write_bit(true); bw.write_u32(0xA5, 8); bw.write_bit(false); bw.write_u32(11, 5); bw.write_u32(2, 2);
write_variable_bits(&mut bw, 8, 1); bw.write_u32(0xEE, 8);
bw.write_u32(0, 5); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_emdf_payloads_substream(&mut br).unwrap();
assert_eq!(parsed.payloads.len(), 1);
let cfg = &parsed.payloads[0].config;
assert!(cfg.b_smpoffst);
assert_eq!(cfg.smpoffst, Some(5));
assert!(cfg.b_duration);
assert_eq!(cfg.duration, Some(12));
assert!(cfg.b_groupid);
assert_eq!(cfg.groupid, Some(2));
assert!(cfg.b_codecdata);
assert_eq!(cfg.codecdata, Some(0xA5));
assert!(!cfg.b_discard_unknown_payload);
assert!(cfg.b_payload_frame_aligned.is_none());
assert_eq!(cfg.priority, Some(11));
assert_eq!(cfg.proc_allowed, Some(2));
assert_eq!(parsed.payloads[0].payload_bytes, vec![0xEE]);
}
#[test]
fn frame_aligned_branch_with_create_remove_duplicate() {
let mut bw = BitWriter::new();
bw.write_u32(2, 5); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_bit(true); bw.write_bit(true); bw.write_bit(false); bw.write_u32(7, 5); bw.write_u32(1, 2);
write_variable_bits(&mut bw, 8, 0);
bw.write_u32(0, 5); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_emdf_payloads_substream(&mut br).unwrap();
let cfg = &parsed.payloads[0].config;
assert_eq!(cfg.b_payload_frame_aligned, Some(true));
assert_eq!(cfg.b_create_duplicate, Some(true));
assert_eq!(cfg.b_remove_duplicate, Some(false));
assert_eq!(cfg.priority, Some(7));
assert_eq!(cfg.proc_allowed, Some(1));
}
#[test]
fn multiple_payloads_in_sequence() {
let mut bw = BitWriter::new();
for (id, payload) in [(1u32, vec![0x01, 0x02]), (5u32, vec![0xFF])] {
bw.write_u32(id, 5);
write_minimal_config(&mut bw);
write_variable_bits(&mut bw, 8, payload.len() as u32);
for b in &payload {
bw.write_u32(*b as u32, 8);
}
}
bw.write_u32(0, 5); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let parsed = parse_emdf_payloads_substream(&mut br).unwrap();
assert_eq!(parsed.payloads.len(), 2);
assert_eq!(parsed.payloads[0].emdf_payload_id, 1);
assert_eq!(parsed.payloads[0].payload_bytes, vec![0x01, 0x02]);
assert_eq!(parsed.payloads[1].emdf_payload_id, 5);
assert_eq!(parsed.payloads[1].payload_bytes, vec![0xFF]);
}
#[test]
fn malformed_oversize_payload_count_rejected() {
let mut bw = BitWriter::new();
for _ in 0..(MAX_EMDF_PAYLOADS + 1) {
bw.write_u32(1, 5);
write_minimal_config(&mut bw);
write_variable_bits(&mut bw, 8, 0);
}
bw.write_u32(0, 5);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let err = parse_emdf_payloads_substream(&mut br).unwrap_err();
assert!(err.to_string().contains("MAX_EMDF_PAYLOADS"), "got: {err}");
}
}