From 3306c2cee83714d001c27e8576790782646e5ba8 Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:49:53 -0700 Subject: [PATCH] Fix title sizes: use pkt_count*192, fix metadata partition range - Size from source_packet_count * 192 (matches libbluray approach) - metadata_sector_ranges() uses actual metadata partition size from ICB instead of arbitrary +256 margin (fixes Top Gun missing CLIPINF) - Store metadata_sectors in UdfFs - Fix CLPI CPI bit-packed field parsing (partial, EP map still needs work) --- src/clpi.rs | 29 +++++++++++++++++++++++++---- src/disc.rs | 10 +++++----- src/udf.rs | 15 ++++++++++++--- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/clpi.rs b/src/clpi.rs index c4ffd8f..7a83140 100644 --- a/src/clpi.rs +++ b/src/clpi.rs @@ -183,11 +183,32 @@ fn parse_cpi(data: &[u8]) -> Result<(Vec, Vec)> { return Ok((Vec::new(), Vec::new())); } + // Stream PID entry — bit-packed per BD spec (libbluray clpi_parse.c): + // stream_PID: 16 bits → ep_map[2..4] + // reserved: 10 bits ┐ + // EP_stream_type: 4 bits │ ep_map[4..14] = 80 bits + // num_EP_coarse: 16 bits │ (10+4+16+18+32 = 80) + // num_EP_fine: 18 bits │ + // EP_map_start_address: 32 bits ┘ + if ep_map.len() < 16 { return Ok((Vec::new(), Vec::new())); } let _stream_pid = u16::from_be_bytes([ep_map[2], ep_map[3]]); - // ep_map[4..6] = reserved + EP stream type - 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; + + // Read 10 bytes (80 bits) from ep_map[4..14] for bit extraction + // Use two u64s since we need 80 bits + let hi = u64::from_be_bytes([ep_map[4], ep_map[5], ep_map[6], ep_map[7], + ep_map[8], ep_map[9], ep_map[10], ep_map[11]]); + let lo_bytes = [ep_map[12], ep_map[13]]; + + // Bit 0-9: reserved (10) + // Bit 10-13: EP_stream_type (4) + // Bit 14-29: num_coarse (16) + // Bit 30-47: num_fine (18) + // Bit 48-79: EP_map_start (32) — bits 48-63 in hi, bits 64-79 in lo + let num_coarse = ((hi >> 34) & 0xFFFF) as usize; + let num_fine = ((hi >> 16) & 0x3FFFF) as usize; + let ep_map_offset = (((hi & 0xFFFF) as u32) << 16) + | (u16::from_be_bytes(lo_bytes) as u32); + let ep_map_offset = ep_map_offset as usize; // EP map for this stream starts at ep_map_offset relative to ep_map start if ep_map_offset + 4 > ep_map.len() { diff --git a/src/disc.rs b/src/disc.rs index 9d42102..1fb0d92 100644 --- a/src/disc.rs +++ b/src/disc.rs @@ -660,7 +660,7 @@ impl Disc { return None; } - // Parse each clip for EP map → sector extents + // Parse each clip for size and sector extents let mut extents = Vec::new(); let mut total_size: u64 = 0; let clip_count = parsed.play_items.len(); @@ -669,11 +669,11 @@ impl Disc { let clpi_path = format!("/BDMV/CLIPINF/{}.clpi", play_item.clip_id); if let Ok(clpi_data) = udf_fs.read_file(session, &clpi_path) { if let Ok(clip_info) = clpi::parse(&clpi_data) { - // Use EP map to get sector extents for this clip's time range + // Size from source packet count (192 bytes per packet) + total_size += clip_info.source_packet_count as u64 * 192; + + // EP map extents for ripping (sector ranges on disc) let clip_extents = clip_info.get_extents(play_item.in_time, play_item.out_time); - for ext in &clip_extents { - total_size += ext.sector_count as u64 * 2048; - } extents.extend(clip_extents); } } diff --git a/src/udf.rs b/src/udf.rs index 36526dd..7e73c24 100644 --- a/src/udf.rs +++ b/src/udf.rs @@ -33,6 +33,8 @@ pub struct UdfFs { /// Metadata partition start (absolute sector) /// For UDF 2.50 discs, all file/directory references use metadata-relative LBAs metadata_start: u32, + /// Metadata partition size in sectors + metadata_sectors: u32, } /// A directory or file entry. @@ -122,8 +124,10 @@ impl UdfFs { pub fn metadata_sector_ranges(&self, session: &mut DriveSession) -> Result> { let mut ranges = Vec::new(); - // UDF structure: AVDP + VDS + metadata partition + FSD + some margin - ranges.push((0, self.metadata_start + 256)); + // UDF structure: sector 0 through end of metadata partition + // Covers AVDP, VDS, partition descriptor, metadata ICB, FSD, all directories + let meta_end = self.metadata_start + self.metadata_sectors; + ranges.push((0, meta_end)); // Walk tree, collect ranges for each metadata file self.collect_file_ranges(session, &self.root, &mut ranges)?; @@ -246,6 +250,7 @@ pub fn read_filesystem(session: &mut DriveSession) -> Result { let mut num_partition_maps: u32 = 0; let mut lvd_sector: Option = None; let mut volume_id = String::new(); + let mut metadata_size_bytes: u32 = 0; for i in 32..64 { let mut desc = [0u8; 2048]; @@ -310,8 +315,9 @@ pub fn read_filesystem(session: &mut DriveSession) -> Result { let l_ea = u32::from_le_bytes([meta_icb[208], meta_icb[209], meta_icb[210], meta_icb[211]]) as usize; let ad_off = 216 + l_ea; - let _ad_len = u32::from_le_bytes([meta_icb[ad_off], meta_icb[ad_off + 1], + let ad_len = u32::from_le_bytes([meta_icb[ad_off], meta_icb[ad_off + 1], meta_icb[ad_off + 2], meta_icb[ad_off + 3]]) & 0x3FFFFFFF; + metadata_size_bytes = ad_len; let ad_pos = u32::from_le_bytes([meta_icb[ad_off + 4], meta_icb[ad_off + 5], meta_icb[ad_off + 6], meta_icb[ad_off + 7]]); // Metadata content starts at partition_start + ad_pos @@ -350,11 +356,14 @@ pub fn read_filesystem(session: &mut DriveSession) -> Result { // Step 5: Read root directory and build file tree let root = read_directory(session, partition_start, metadata_start, root_lba, "", 0)?; + let metadata_sectors = (metadata_size_bytes + 2047) / 2048; + Ok(UdfFs { root, volume_id, partition_start, metadata_start, + metadata_sectors, }) }