Introduce libdvdcss wrapper.
- This is here for devices that may falsely request bus encryption due to bit flips in MPEG-2 PACK headers. - Not yet made an optional feature; need to figure out how to even talk with a drive over SCSI.
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "libdvdcss"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
derive_more.workspace = true
|
||||
semver.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
@@ -0,0 +1,3 @@
|
||||
# libdvdcss Rust Wrapper
|
||||
|
||||
Minimum `libdvdcss` version: 1.6.0
|
||||
@@ -0,0 +1,24 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Path from which to search for shared libraries.
|
||||
// Is there no better (automated), platform-specific way?
|
||||
println!("cargo:rustc-link-search=/usr/lib");
|
||||
|
||||
// Locate and link the shared library.
|
||||
println!("cargo:rustc-link-lib=libdvdcss");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("src/wrapper.h")
|
||||
// Invalidate the build if a header has changed.
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Failed to generate bindings.");
|
||||
|
||||
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
@@ -0,0 +1,17 @@
|
||||
use derive_more::{Display, From};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, From, Display)]
|
||||
pub enum Error {
|
||||
#[display("I32ToEnumCastError {{ {}, {} }}", value, enum_)]
|
||||
I32ToEnumCastError {
|
||||
value: i32,
|
||||
enum_: String,
|
||||
},
|
||||
InsufficientBufferLength,
|
||||
ReadError {
|
||||
value: i32,
|
||||
},
|
||||
SeekError,
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
use std::ffi::{CStr, c_int, c_void};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
mod dvdcss;
|
||||
mod error;
|
||||
|
||||
use dvdcss::*;
|
||||
use semver::Version;
|
||||
|
||||
pub const BLOCK_SIZE: u32 = DVDCSS_BLOCK_SIZE;
|
||||
pub const DVDCSS_VERSION: semver::Version = Version::new(
|
||||
DVDCSS_VERSION_MAJOR as u64,
|
||||
DVDCSS_VERSION_MINOR as u64,
|
||||
DVDCSS_VERSION_MICRO as u64,
|
||||
);
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum SeekFlag {
|
||||
#[default]
|
||||
None = DVDCSS_NOFLAGS,
|
||||
Key = DVDCSS_SEEK_KEY,
|
||||
Mpeg = DVDCSS_SEEK_MPEG,
|
||||
}
|
||||
|
||||
// I dislike this as technically, 2 should be Unknown/Reserved and 3 as CPRM.
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum EncryptionScheme {
|
||||
#[default]
|
||||
None = 0,
|
||||
CssCppm = 1,
|
||||
//Reserved,
|
||||
Cprm = 2,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl TryFrom<i32> for EncryptionScheme {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: i32) -> std::result::Result<Self, Self::Error> {
|
||||
if !(0..=2).contains(&value) {
|
||||
return Err(Error::I32ToEnumCastError {
|
||||
value,
|
||||
enum_: "EncryptionScheme".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(match value {
|
||||
0 => Self::None,
|
||||
1 => Self::CssCppm,
|
||||
2 => Self::Cprm,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct DVD {
|
||||
ptr: dvdcss_t,
|
||||
}
|
||||
|
||||
impl DVD {
|
||||
pub fn new<P: AsRef<Path>>(src: P) -> Option<Self> {
|
||||
// Cast the path to a CStr (*const char), returning early if there's any issue.
|
||||
let c_path = CStr::from_bytes_with_nul(src.as_ref().as_os_str().as_encoded_bytes())
|
||||
.ok()?
|
||||
.as_ptr();
|
||||
|
||||
// Make the FFI call.
|
||||
let ptr = unsafe { dvdcss_open(c_path) };
|
||||
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Self { ptr })
|
||||
}
|
||||
}
|
||||
|
||||
// Seek in blocks (See: `BLOCK_SIZE`).
|
||||
pub fn seek(&self, blocks: u32, flag: SeekFlag) -> Result<u32> {
|
||||
// Although this call generally returns a negative value on fail, as of writing
|
||||
// it can only return -1, so there's no point in expanding this fail result into
|
||||
// a proper enum.
|
||||
let retval = unsafe { dvdcss_seek(self.ptr, blocks as c_int, flag as c_int) };
|
||||
|
||||
if retval < 0 {
|
||||
Err(Error::SeekError)
|
||||
} else {
|
||||
Ok(retval as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8], blocks: u32, decrypt: bool) -> Result<u32> {
|
||||
// If the user has provided a wonk buffer size that's not a multiple of BLOCK_SIZE,
|
||||
// I'm not fixing it for them.
|
||||
// But ensure that buffer is big enough to safely read the data.
|
||||
if buf.len() < (blocks * BLOCK_SIZE) as usize {
|
||||
return Err(Error::InsufficientBufferLength);
|
||||
}
|
||||
|
||||
let i_flags = if decrypt {
|
||||
DVDCSS_READ_DECRYPT
|
||||
} else {
|
||||
DVDCSS_NOFLAGS
|
||||
} as i32;
|
||||
|
||||
let retval = unsafe {
|
||||
dvdcss_read(
|
||||
self.ptr,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
blocks as c_int,
|
||||
i_flags,
|
||||
)
|
||||
};
|
||||
|
||||
if retval < 0 {
|
||||
// I can't track back the error return values for pf_read,
|
||||
// so this is good enough for now.
|
||||
return Err(Error::ReadError { value: retval });
|
||||
}
|
||||
|
||||
Ok(retval as u32)
|
||||
}
|
||||
|
||||
// Return the error message string from dvdcss_error
|
||||
pub fn error(&self) -> &CStr {
|
||||
unsafe { CStr::from_ptr(dvdcss_error(self.ptr)) }
|
||||
}
|
||||
|
||||
// Return the error message string from dvdcss_error with lossy conversion.
|
||||
pub fn error_lossy(&self) -> String {
|
||||
self.error().to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
pub fn is_scrambled(&self) -> bool {
|
||||
match unsafe { dvdcss_is_scrambled(self.ptr) } {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn encryption_type(&self) -> EncryptionScheme {
|
||||
unsafe { dvdcss_get_encryption_type(self.ptr) }
|
||||
.try_into()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl Drop for DVD {
|
||||
fn drop(&mut self) {
|
||||
// Right... so drop can't handle return values.
|
||||
// So... best option is ...panic?
|
||||
let retval = unsafe { dvdcss_close(self.ptr) };
|
||||
assert!(retval == 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <dvdcss/dvdcss.h>
|
||||
Reference in New Issue
Block a user