diff --git a/chd-rs-capi/chd.h b/chd-rs-capi/chd.h index 8dcdcb5..30bac54 100644 --- a/chd-rs-capi/chd.h +++ b/chd-rs-capi/chd.h @@ -166,6 +166,31 @@ chd_error chd_codec_config(const struct chd_file *_chd, int32_t _param, void *_c chd_error chd_read_header(const char *filename, struct chd_header *header); +/** + * Read CHD header data from the file into the pointed struct. + * + * Ownership of the core_file is retained by the caller when calling this function. + * + * # Safety + * * `filename` is a valid, null-terminated **UTF-8** string. + * * `header` is either `NULL`, or an aligned pointer to a possibly uninitialized `chd_header` struct. + * * If `header` is `NULL`, returns `CHDERR_INVALID_PARAMETER` + */ +chd_error chd_read_header_file(core_file *file, + struct chd_header *header); + +/** + * Read CHD header data from the file into the pointed struct. + * + * Ownership is retained by the caller when calling this function. + * + * # Safety + * * `file` is a valid pointer to a `core_file` with respect to the implementation of libchdcorefile that was linked. + * * `header` is either `NULL`, or an aligned pointer to a possibly uninitialized `chd_header` struct. + */ +chd_error chd_read_header_core_file(core_file *file, + struct chd_header *header); + /** * Returns the associated `core_file*`. * diff --git a/chd-rs-capi/src/chdcorefile.rs b/chd-rs-capi/src/chdcorefile.rs index 768e0b3..b81f410 100644 --- a/chd-rs-capi/src/chdcorefile.rs +++ b/chd-rs-capi/src/chdcorefile.rs @@ -16,13 +16,7 @@ impl SeekRead for CoreFile { impl Read for CoreFile { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let res = unsafe { - core_fread( - self.file, - buf.as_mut_ptr() as *mut c_void, - buf.len(), - ) - }; + let res = unsafe { core_fread(self.file, buf.as_mut_ptr() as *mut c_void, buf.len()) }; Ok(res) } } @@ -45,6 +39,8 @@ mod tests { use crate::chdcorefile_sys::core_fopen; use std::fs::File; use std::io::{Read, Write}; + use std::mem::MaybeUninit; + use crate::chd_read_header_core_file; #[test] fn chdcorefile_read() { @@ -59,6 +55,27 @@ mod tests { file.read_exact(&mut buf).unwrap(); assert_eq!(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], &buf); } + + #[test] + fn chdcorefile_read_header() { + let file = unsafe { + core_fopen( + b"../chd-rs/.testimages/cliffhgr.chd\0".as_ptr() as *const std::os::raw::c_char + ) + }; + + unsafe { + let mut header = MaybeUninit::zeroed(); + chd_read_header_core_file(file, &mut header); + let header = header.assume_init(); + eprintln!("{:?}", header); + + } + + // let mut buf = [0u8; 10]; + // file.read_exact(&mut buf).unwrap(); + // assert_eq!(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], &buf); + } } impl Drop for CoreFile { diff --git a/chd-rs-capi/src/header.rs b/chd-rs-capi/src/header.rs index 3f592ad..4d0a0e3 100644 --- a/chd-rs-capi/src/header.rs +++ b/chd-rs-capi/src/header.rs @@ -10,6 +10,7 @@ pub const CHD_SHA1_BYTES: usize = 20; #[allow(non_camel_case_types)] /// libchdr-compatible CHD header struct. /// This struct is ABI-compatible with [chd.h](https://github.com/rtissera/libchdr/blob/cdcb714235b9ff7d207b703260706a364282b063/include/libchdr/chd.h#L302) +#[derive(Debug)] pub struct chd_header { length: u32, version: u32, diff --git a/chd-rs-capi/src/lib.rs b/chd-rs-capi/src/lib.rs index 78eefef..635f367 100644 --- a/chd-rs-capi/src/lib.rs +++ b/chd-rs-capi/src/lib.rs @@ -358,6 +358,65 @@ pub unsafe extern "C" fn chd_read_header( } } +#[no_mangle] +#[cfg(feature = "chd_core_file")] +#[cfg_attr(docsrs, doc(cfg(chd_core_file)))] +/// Read CHD header data from the file into the pointed struct. +/// +/// Ownership of the core_file is retained by the caller when calling this function. +/// +/// # Safety +/// * `filename` is a valid, null-terminated **UTF-8** string. +/// * `header` is either `NULL`, or an aligned pointer to a possibly uninitialized `chd_header` struct. +/// * If `header` is `NULL`, returns `CHDERR_INVALID_PARAMETER` +pub unsafe extern "C" fn chd_read_header_file( + file: *mut chdcorefile_sys::core_file, + header: *mut MaybeUninit, +) -> chd_error { + let core_file = Box::new(crate::chdcorefile::CoreFile { file }) as Box; + let chd = match Chd::open(core_file, None) { + Ok(chd) => chd, + Err(e) => return e, + }; + + let chd_header = ffi_chd_get_header(&chd); + let result = match unsafe { header.as_mut() } { + None => Error::InvalidParameter, + Some(header) => { + header.write(chd_header); + Error::None + } + }; + + let (file, _) = chd.into_inner(); + let file = file as Box; + let Ok(file) = file.downcast::() else { + return Error::Unknown; + }; + + let file = *file; + std::mem::forget(file); + + result +} + +#[no_mangle] +#[cfg(feature = "chd_virtio")] +#[cfg_attr(docsrs, doc(cfg(chd_virtio)))] +/// Read CHD header data from the file into the pointed struct. +/// +/// Ownership is retained by the caller when calling this function. +/// +/// # Safety +/// * `file` is a valid pointer to a `core_file` with respect to the implementation of libchdcorefile that was linked. +/// * `header` is either `NULL`, or an aligned pointer to a possibly uninitialized `chd_header` struct. +pub unsafe extern "C" fn chd_read_header_core_file( + file: *mut chdcorefile_sys::core_file, + header: *mut MaybeUninit, +) -> chd_error { + unsafe { chd_read_header_file(file, header) } +} + #[no_mangle] #[cfg(feature = "chd_core_file")] #[cfg_attr(docsrs, doc(cfg(chd_core_file)))] @@ -431,7 +490,7 @@ pub unsafe extern "C" fn chd_open_file( Some(ffi_takeown_chd(parent)) }; - let core_file = Box::new(crate::chdcorefile::CoreFile { file: file }) as Box; + let core_file = Box::new(crate::chdcorefile::CoreFile { file }) as Box; let chd = match Chd::open(core_file, parent) { Ok(chd) => chd, Err(e) => return e, @@ -472,6 +531,7 @@ pub extern "C" fn chd_get_codec_name(_codec: u32) -> *const c_char { b"Unknown\0".as_ptr() as *const c_char } +use crate::chdcorefile::CoreFile; #[cfg(feature = "chd_precache")] use std::io::SeekFrom; diff --git a/chd-rs/src/chdfile.rs b/chd-rs/src/chdfile.rs index b7788a4..211462b 100644 --- a/chd-rs/src/chdfile.rs +++ b/chd-rs/src/chdfile.rs @@ -381,7 +381,6 @@ impl<'a, F: Read + Seek> Hunk<'a, F> { /// [`Error::RequiresParent`](crate::Error::RequiresParent). /// /// If the provided output buffer is the wrong length, this will return - /// If the hunk refers to a parent CHD that was not provided, this will return /// [`Error::OutOfMemory`](crate::Error::OutOfMemory). pub fn read_hunk_in( &mut self,