use playready::{cdm::Session, pssh::WrmHeader, Cdm, Device, Pssh};
use safer_ffi::prelude::*;
#[derive_ReprC(rename = "playready_cdm")]
#[repr(opaque)]
pub struct FfiCdm {
inner: Cdm,
}
#[derive_ReprC(rename = "playready_session")]
#[repr(opaque)]
pub struct FfiSession {
inner: Session,
}
#[derive_ReprC(rename = "playready_pssh")]
#[repr(opaque)]
pub struct FfiPssh {
inner: Pssh,
}
#[derive_ReprC(rename = "playready_wrm_header")]
#[repr(opaque)]
pub struct FfiWrmHeader {
inner: WrmHeader,
}
impl From<WrmHeader> for FfiWrmHeader {
fn from(value: WrmHeader) -> Self {
Self { inner: value }
}
}
#[derive_ReprC(rename = "playready_kid_ck")]
#[repr(C)]
pub struct FfiKidCk {
pub kid: [u8; 16],
pub ck: repr_c::Box<[u8]>,
}
#[ffi_export]
pub fn playready_cdm_create_from_prd(
prd_path: char_p::Ref<'_>,
error_msg: Out<'_, Option<char_p::Box>>,
) -> Option<repr_c::Box<FfiCdm>> {
let device = match Device::from_prd(prd_path.to_str()) {
Ok(device) => device,
Err(err) => {
error_msg.write(Some(char_p::new(format!("{err:?}"))));
return None;
}
};
Some(
Box::new(FfiCdm {
inner: Cdm::from_device(device),
})
.into(),
)
}
#[ffi_export]
pub fn playready_cdm_open_session(cdm: &FfiCdm) -> repr_c::Box<FfiSession> {
Box::new(FfiSession {
inner: cdm.inner.open_session(),
})
.into()
}
#[ffi_export]
pub fn playready_pssh_from_bytes(
bytes: c_slice::Ref<'_, u8>,
error_msg: Out<'_, Option<char_p::Box>>,
) -> Option<repr_c::Box<FfiPssh>> {
match Pssh::from_bytes(bytes.as_slice()) {
Ok(inner) => Some(Box::new(FfiPssh { inner }).into()),
Err(err) => {
error_msg.write(Some(char_p::new(format!("{err:?}"))));
None
}
}
}
#[ffi_export]
pub fn playready_pssh_from_b64(
b64: char_p::Ref<'_>,
error_msg: Out<'_, Option<char_p::Box>>,
) -> Option<repr_c::Box<FfiPssh>> {
match Pssh::from_b64(b64.to_bytes()) {
Ok(inner) => Some(Box::new(FfiPssh { inner }).into()),
Err(err) => {
error_msg.write(Some(char_p::new(format!("{err:?}"))));
None
}
}
}
#[ffi_export]
pub fn playready_pssh_get_first_wrm_header(pssh: &FfiPssh) -> Option<repr_c::Box<FfiWrmHeader>> {
let wrm_headers = pssh.inner.wrm_headers();
let wrm_header: FfiWrmHeader = wrm_headers.first().cloned()?.into();
Some(Box::new(wrm_header).into())
}
#[ffi_export]
pub fn playready_session_get_license_challenge(
session: &FfiSession,
wrm_header: repr_c::Box<FfiWrmHeader>,
error_msg: Out<'_, Option<char_p::Box>>,
) -> Option<char_p::Box> {
let wrm_header = wrm_header.into();
match session.inner.get_license_challenge(wrm_header.inner) {
Ok(s) => Some(char_p::new(s)),
Err(err) => {
error_msg.write(Some(char_p::new(format!("{err:?}"))));
None
}
}
}
#[ffi_export]
pub fn playready_session_get_keys_from_challenge_response(
session: &FfiSession,
response: char_p::Ref<'_>,
error_msg: Out<'_, Option<char_p::Box>>,
) -> Option<repr_c::Vec<FfiKidCk>> {
match session
.inner
.get_keys_from_challenge_response(response.to_str())
{
Ok(keys) => Some(
keys.into_iter()
.map(|kid_ck| FfiKidCk {
kid: kid_ck.0.into(),
ck: Box::<[u8]>::from(kid_ck.1).into(),
})
.collect::<Vec<_>>()
.into(),
),
Err(err) => {
error_msg.write(Some(char_p::new(format!("{err:?}"))));
None
}
}
}
#[ffi_export]
pub fn playready_cdm_free(cdm: Option<repr_c::Box<FfiCdm>>) {
drop(cdm)
}
#[ffi_export]
pub fn playready_session_free(session: Option<repr_c::Box<FfiSession>>) {
drop(session)
}
#[ffi_export]
pub fn playready_pssh_free(pssh: Option<repr_c::Box<FfiPssh>>) {
drop(pssh)
}
#[ffi_export]
pub fn playready_license_challenge_free(challenge: Option<char_p::Box>) {
drop(challenge)
}
#[ffi_export]
pub fn playready_keys_free(keys: repr_c::Vec<FfiKidCk>) {
drop(keys)
}
#[ffi_export]
pub fn playready_error_message_free(error_msg: Option<char_p::Box>) {
drop(error_msg)
}
pub fn generate_headers() -> std::io::Result<()> {
safer_ffi::headers::builder()
.to_file("playready.h")?
.generate()
}