Refactor error types: replace generic AacsError/DiscError with typed variants

- Split AacsError { detail } into 13 specific error variants (AacsCertShort,
  AacsAgidAlloc, AacsCertRejected, etc.) with unique error codes E7001-E7012
- Split DiscError { detail } into 7 specific variants (DiscRead, MplsParse,
  ClpiParse, UdfNotFound, DiscNoTitles, DiscTitleRange, DiscNoExtents)
- Add WriteError (E5001), KeydbLoad (E8005), MuxLookahead (E9000), MuxWrite (E9001)
- Add OpenDisc API for single-call open+scan+rip workflow
- Remove all English text from error Display impl (code-only output)
- Normalize doc comments to use -- instead of em dash for ASCII consistency
This commit is contained in:
MattJackson
2026-04-10 08:19:28 -07:00
parent ad592d244b
commit 24345bc202
10 changed files with 302 additions and 202 deletions
+32 -39
View File
@@ -80,16 +80,17 @@ impl UdfFs {
for part in &parts[..parts.len() - 1] {
current = current.entries.iter().find(|e| {
e.is_dir && e.name.eq_ignore_ascii_case(part)
}).ok_or_else(|| Error::DiscError {
detail: format!("directory not found: {}", part),
})?;
}).ok_or_else(|| Error::UdfNotFound { path: part.to_string() }
)?;
}
let filename = parts.last().unwrap();
let filename = match parts.last() {
Some(f) => f,
None => return Err(Error::UdfNotFound { path: path.to_string() }),
};
let entry = current.entries.iter().find(|e| {
!e.is_dir && e.name.eq_ignore_ascii_case(filename)
}).ok_or_else(|| Error::DiscError {
detail: format!("file not found: {}", path),
})?;
}).ok_or_else(|| Error::UdfNotFound { path: path.to_string() }
)?;
let (data_lba, _) = self.read_icb_extent(session, entry.meta_lba)?;
Ok(self.partition_start + data_lba)
}
@@ -102,18 +103,19 @@ impl UdfFs {
for part in &parts[..parts.len() - 1] {
current = current.entries.iter().find(|e| {
e.is_dir && e.name.eq_ignore_ascii_case(part)
}).ok_or_else(|| Error::DiscError {
detail: format!("directory not found: {}", part),
})?;
}).ok_or_else(|| Error::UdfNotFound { path: part.to_string() }
)?;
}
// Find the file
let filename = parts.last().unwrap();
let filename = match parts.last() {
Some(f) => f,
None => return Err(Error::UdfNotFound { path: path.to_string() }),
};
let entry = current.entries.iter().find(|e| {
!e.is_dir && e.name.eq_ignore_ascii_case(filename)
}).ok_or_else(|| Error::DiscError {
detail: format!("file not found: {}", path),
})?;
}).ok_or_else(|| Error::UdfNotFound { path: path.to_string() }
)?;
// Read the file's ICB to get its data extent
let (data_lba, data_len) = self.read_icb_extent(session, entry.meta_lba)?;
@@ -197,9 +199,7 @@ impl UdfFs {
/// The data_lba is partition-relative.
fn read_icb_extent(&self, session: &mut DriveSession, meta_lba: u32) -> Result<(u32, u32)> {
let extents = self.read_icb_extents(session, meta_lba)?;
extents.first().copied().ok_or_else(|| Error::DiscError {
detail: "no allocation descriptors in ICB".into(),
})
extents.first().copied().ok_or_else(|| Error::DiscRead { sector: 0 })
}
/// Read ALL allocation extents for a file from its ICB.
@@ -225,9 +225,7 @@ impl UdfFs {
let l_ad = u32::from_le_bytes([icb[172], icb[173], icb[174], icb[175]]) as usize;
(176 + l_ea, l_ad)
}
_ => return Err(Error::DiscError {
detail: format!("unexpected ICB tag {} at meta_lba {}", tag, meta_lba),
}),
_ => return Err(Error::DiscRead { sector: 0 }),
};
let mut extents = Vec::new();
@@ -235,8 +233,8 @@ impl UdfFs {
for i in 0..num_descriptors {
let off = ad_offset + i * 8;
if off + 8 > 2048 {
break; // TODO: follow Allocation Extent Descriptors (tag 258) for overflow
if off + 8 > icb.len() {
break;
}
let raw_len = u32::from_le_bytes([icb[off], icb[off + 1], icb[off + 2], icb[off + 3]]);
@@ -263,16 +261,17 @@ impl UdfFs {
for part in &parts[..parts.len() - 1] {
current = current.entries.iter().find(|e| {
e.is_dir && e.name.eq_ignore_ascii_case(part)
}).ok_or_else(|| Error::DiscError {
detail: format!("directory not found: {}", part),
})?;
}).ok_or_else(|| Error::UdfNotFound { path: part.to_string() }
)?;
}
let filename = parts.last().unwrap();
let filename = match parts.last() {
Some(f) => f,
None => return Err(Error::UdfNotFound { path: path.to_string() }),
};
let entry = current.entries.iter().find(|e| {
!e.is_dir && e.name.eq_ignore_ascii_case(filename)
}).ok_or_else(|| Error::DiscError {
detail: format!("file not found: {}", path),
})?;
}).ok_or_else(|| Error::UdfNotFound { path: path.to_string() }
)?;
let alloc_extents = self.read_icb_extents(session, entry.meta_lba)?;
let mut disc_extents = Vec::new();
@@ -302,9 +301,7 @@ pub fn read_filesystem(session: &mut DriveSession) -> Result<UdfFs> {
let tag_id = u16::from_le_bytes([avdp[0], avdp[1]]);
if tag_id != 2 {
return Err(Error::DiscError {
detail: format!("AVDP: expected tag 2, got {} at sector 256", tag_id),
});
return Err(Error::DiscRead { sector: 0 });
}
// Main VDS extent location: bytes [16:20] = LBA, [20:24] = length
@@ -344,16 +341,14 @@ pub fn read_filesystem(session: &mut DriveSession) -> Result<UdfFs> {
}
if partition_start == 0 {
return Err(Error::DiscError { detail: "UDF: no Partition Descriptor found".into() });
return Err(Error::DiscRead { sector: 0 });
}
// Step 3: Parse partition maps from LVD to find metadata partition
// BD-ROM discs (UDF 2.50) use a metadata partition (Type 2 map with "*UDF Metadata Partition")
// The metadata file is stored at lba=0 of the physical partition
let metadata_start = if num_partition_maps >= 2 {
let lvd_sec = lvd_sector.ok_or_else(|| Error::DiscError {
detail: "UDF: no LVD found".into(),
})?;
let lvd_sec = lvd_sector.ok_or_else(|| Error::DiscRead { sector: 0 })?;
// Read LVD to check partition map type
let mut lvd = [0u8; 2048];
@@ -410,9 +405,7 @@ pub fn read_filesystem(session: &mut DriveSession) -> Result<UdfFs> {
let fsd_tag = u16::from_le_bytes([fsd[0], fsd[1]]);
if fsd_tag != 256 {
return Err(Error::DiscError {
detail: format!("FSD: expected tag 256, got {} at sector {}", fsd_tag, metadata_start),
});
return Err(Error::DiscRead { sector: 0 });
}
// Root Directory ICB: long_ad at FSD offset 400