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)
This commit is contained in:
+25
-4
@@ -183,11 +183,32 @@ fn parse_cpi(data: &[u8]) -> Result<(Vec<EpCoarse>, Vec<EpFine>)> {
|
||||
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() {
|
||||
|
||||
+5
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -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<Vec<(u32, u32)>> {
|
||||
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<UdfFs> {
|
||||
let mut num_partition_maps: u32 = 0;
|
||||
let mut lvd_sector: Option<u32> = 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<UdfFs> {
|
||||
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<UdfFs> {
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user