use crate::error::{Error, Result};
use crate::disc::Extent;
#[derive(Debug)]
pub struct ClipInfo {
pub version: String,
pub source_packet_count: u32,
pub ep_coarse: Vec<EpCoarse>,
pub ep_fine: Vec<EpFine>,
}
#[derive(Debug, Clone)]
pub struct EpCoarse {
pub ref_to_fine_id: u32,
pub pts_coarse: u32,
pub spn_coarse: u32,
}
#[derive(Debug, Clone)]
pub struct EpFine {
pub pts_fine: u32,
pub spn_fine: u32,
}
impl ClipInfo {
pub fn full_pts(coarse: &EpCoarse, fine: &EpFine) -> u32 {
(coarse.pts_coarse << 19) + (fine.pts_fine << 8)
}
pub fn full_spn(coarse: &EpCoarse, fine: &EpFine) -> u32 {
(coarse.spn_coarse & 0xFFFE0000) + fine.spn_fine
}
pub fn resolved_ep_map(&self) -> Vec<(u32, u32)> {
let mut entries = Vec::new();
for (ci, coarse) in self.ep_coarse.iter().enumerate() {
let fine_start = coarse.ref_to_fine_id as usize;
let fine_end = if ci + 1 < self.ep_coarse.len() {
self.ep_coarse[ci + 1].ref_to_fine_id as usize
} else {
self.ep_fine.len()
};
for fi in fine_start..fine_end.min(self.ep_fine.len()) {
let fine = &self.ep_fine[fi];
let pts = Self::full_pts(coarse, fine);
let spn = Self::full_spn(coarse, fine);
entries.push((pts, spn));
}
}
entries
}
pub fn get_extents(&self, in_time: u32, out_time: u32) -> Vec<Extent> {
let ep_map = self.resolved_ep_map();
if ep_map.is_empty() {
return Vec::new();
}
let start_spn = match ep_map.binary_search_by_key(&in_time, |(pts, _)| *pts) {
Ok(i) => ep_map[i].1,
Err(0) => ep_map[0].1,
Err(i) => ep_map[i - 1].1,
};
let end_spn = match ep_map.binary_search_by_key(&out_time, |(pts, _)| *pts) {
Ok(i) => ep_map[i].1,
Err(i) if i < ep_map.len() => ep_map[i].1,
_ => ep_map.last().unwrap().1 + 1,
};
if end_spn <= start_spn {
return Vec::new();
}
let start_byte = start_spn as u64 * 192;
let end_byte = end_spn as u64 * 192;
let start_sector = (start_byte / 2048) as u32;
let end_sector = ((end_byte + 2047) / 2048) as u32;
vec![Extent {
start_lba: start_sector, sector_count: end_sector - start_sector,
}]
}
}
pub fn parse(data: &[u8]) -> Result<ClipInfo> {
if data.len() < 40 {
return Err(Error::DiscError { detail: "CLPI too short".into() });
}
if &data[0..4] != b"HDMV" {
return Err(Error::DiscError { detail: "not a CLPI file".into() });
}
let version = String::from_utf8_lossy(&data[4..8]).to_string();
let _seq_info_start = u32::from_be_bytes([data[8], data[9], data[10], data[11]]) as usize;
let _prog_info_start = u32::from_be_bytes([data[12], data[13], data[14], data[15]]) as usize;
let cpi_start = u32::from_be_bytes([data[16], data[17], data[18], data[19]]) as usize;
let source_packet_count = if data.len() > 56 {
u32::from_be_bytes([data[56], data[57], data[58], data[59]])
} else {
0
};
let (ep_coarse, ep_fine) = if cpi_start > 0 && cpi_start + 8 < data.len() {
parse_cpi(&data[cpi_start..])?
} else {
(Vec::new(), Vec::new())
};
Ok(ClipInfo {
version,
source_packet_count,
ep_coarse,
ep_fine,
})
}
fn parse_cpi(data: &[u8]) -> Result<(Vec<EpCoarse>, Vec<EpFine>)> {
if data.len() < 8 {
return Ok((Vec::new(), Vec::new()));
}
let cpi_length = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize;
if cpi_length < 4 {
return Ok((Vec::new(), Vec::new()));
}
let ep_map = &data[6..];
if ep_map.len() < 4 {
return Ok((Vec::new(), Vec::new()));
}
let num_streams = ep_map[1] as usize;
if num_streams == 0 {
return Ok((Vec::new(), Vec::new()));
}
if ep_map.len() < 16 {
return Ok((Vec::new(), Vec::new()));
}
let _stream_pid = u16::from_be_bytes([ep_map[2], ep_map[3]]);
let num_coarse = u16::from_be_bytes([ep_map[6], ep_map[7]]) as usize;
let num_fine = u32::from_be_bytes([ep_map[8], ep_map[9], ep_map[10], ep_map[11]]) as usize;
let ep_map_offset = u32::from_be_bytes([ep_map[12], ep_map[13], ep_map[14], ep_map[15]]) as usize;
if ep_map_offset + 4 > ep_map.len() {
return Ok((Vec::new(), Vec::new()));
}
let stream_ep = &ep_map[ep_map_offset..];
if stream_ep.len() < 4 {
return Ok((Vec::new(), Vec::new()));
}
let fine_start = u32::from_be_bytes([stream_ep[0], stream_ep[1], stream_ep[2], stream_ep[3]]) as usize;
let coarse_data = &stream_ep[4..];
let mut ep_coarse = Vec::with_capacity(num_coarse);
for i in 0..num_coarse {
let off = i * 8;
if off + 8 > coarse_data.len() {
break;
}
let dword0 = u32::from_be_bytes([coarse_data[off], coarse_data[off + 1],
coarse_data[off + 2], coarse_data[off + 3]]);
let ref_to_fine_id = dword0 >> 14;
let pts_coarse = dword0 & 0x3FFF;
let spn_coarse = u32::from_be_bytes([coarse_data[off + 4], coarse_data[off + 5],
coarse_data[off + 6], coarse_data[off + 7]]);
ep_coarse.push(EpCoarse {
ref_to_fine_id,
pts_coarse,
spn_coarse,
});
}
let mut ep_fine = Vec::with_capacity(num_fine);
if fine_start < stream_ep.len() {
let fine_data = &stream_ep[fine_start..];
for i in 0..num_fine {
let off = i * 4;
if off + 4 > fine_data.len() {
break;
}
let dword = u32::from_be_bytes([fine_data[off], fine_data[off + 1],
fine_data[off + 2], fine_data[off + 3]]);
let pts_fine = (dword >> 17) & 0x7FF;
let spn_fine = dword & 0x1FFFF;
ep_fine.push(EpFine { pts_fine, spn_fine });
}
}
Ok((ep_coarse, ep_fine))
}