324 lines
10 KiB
Rust
324 lines
10 KiB
Rust
//! Bindings for [`AAsset`], [`AAssetDir`] and [`AAssetManager`]
|
|
//!
|
|
//! [`AAsset`]: https://developer.android.com/ndk/reference/group/asset#aasset
|
|
//! [`AAssetDir`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
|
|
//! [`AAssetManager`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
|
|
|
|
use std::{
|
|
ffi::{CStr, CString},
|
|
io,
|
|
os::fd::{FromRawFd, OwnedFd},
|
|
ptr::NonNull,
|
|
};
|
|
|
|
/// A native [`AAssetManager *`]
|
|
///
|
|
/// [`AAssetManager *`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
|
|
#[derive(Debug)]
|
|
#[doc(alias = "AAssetManager")]
|
|
pub struct AssetManager {
|
|
ptr: NonNull<ffi::AAssetManager>,
|
|
}
|
|
|
|
// AAssetManager is thread safe.
|
|
// See https://developer.android.com/ndk/reference/group/asset#aassetmanager
|
|
unsafe impl Send for AssetManager {}
|
|
unsafe impl Sync for AssetManager {}
|
|
|
|
impl AssetManager {
|
|
/// Create an `AssetManager` from a pointer
|
|
///
|
|
/// # Safety
|
|
/// By calling this function, you assert that the pointer is a valid pointer to a native
|
|
/// `AAssetManager`.
|
|
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetManager>) -> Self {
|
|
Self { ptr }
|
|
}
|
|
|
|
/// Returns the pointer to the native `AAssetManager`.
|
|
pub fn ptr(&self) -> NonNull<ffi::AAssetManager> {
|
|
self.ptr
|
|
}
|
|
|
|
/// Open the asset. Returns [`None`] if opening the asset fails.
|
|
///
|
|
/// This currently always opens the asset in the streaming mode.
|
|
#[doc(alias = "AAssetManager_open")]
|
|
pub fn open(&self, filename: &CStr) -> Option<Asset> {
|
|
unsafe {
|
|
let ptr = ffi::AAssetManager_open(
|
|
self.ptr.as_ptr(),
|
|
filename.as_ptr(),
|
|
ffi::AASSET_MODE_STREAMING as i32,
|
|
);
|
|
Some(Asset::from_ptr(NonNull::new(ptr)?))
|
|
}
|
|
}
|
|
|
|
/// Open an asset directory. Returns [`None`] if opening the directory fails.
|
|
#[doc(alias = "AAssetManager_openDir")]
|
|
pub fn open_dir(&self, filename: &CStr) -> Option<AssetDir> {
|
|
unsafe {
|
|
let ptr = ffi::AAssetManager_openDir(self.ptr.as_ptr(), filename.as_ptr());
|
|
Some(AssetDir::from_ptr(NonNull::new(ptr)?))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A native [`AAssetDir *`]
|
|
///
|
|
/// ```no_run
|
|
/// # use std::ffi::CString;
|
|
/// # use ndk::asset::AssetManager;
|
|
/// # let asset_manager: AssetManager = unimplemented!();
|
|
/// use std::io::Read;
|
|
///
|
|
/// let mut my_dir = asset_manager
|
|
/// .open_dir(&CString::new("my_dir").unwrap())
|
|
/// .expect("Could not open directory");
|
|
///
|
|
/// // Use it as an iterator
|
|
/// let all_files = my_dir.collect::<Vec<CString>>();
|
|
///
|
|
/// // Reset the iterator
|
|
/// my_dir.rewind();
|
|
///
|
|
/// // Use .with_next() to iterate without allocating `CString`s
|
|
/// while let Some(asset) = my_dir.with_next(|cstr| asset_manager.open(cstr).unwrap()) {
|
|
/// let mut text = String::new();
|
|
/// asset.read_to_string(&mut text);
|
|
/// // ...
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// [`AAssetDir *`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
|
|
#[derive(Debug)]
|
|
#[doc(alias = "AAssetDir")]
|
|
pub struct AssetDir {
|
|
ptr: NonNull<ffi::AAssetDir>,
|
|
}
|
|
|
|
// It's unclear if AAssetDir is thread safe.
|
|
// However, AAsset is not, so there's a good chance that AAssetDir is not either.
|
|
|
|
impl Drop for AssetDir {
|
|
#[doc(alias = "AAssetDir_close")]
|
|
fn drop(&mut self) {
|
|
unsafe { ffi::AAssetDir_close(self.ptr.as_ptr()) }
|
|
}
|
|
}
|
|
|
|
impl AssetDir {
|
|
/// Construct an `AssetDir` from the native `AAssetDir *`. This gives ownership of the
|
|
/// `AAssetDir *` to the `AssetDir`, which will handle closing the asset. Avoid using
|
|
/// the pointer after calling this function.
|
|
///
|
|
/// # Safety
|
|
/// By calling this function, you assert that it points to a valid native `AAssetDir`.
|
|
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetDir>) -> Self {
|
|
Self { ptr }
|
|
}
|
|
|
|
/// The corresponding native `AAssetDir *`
|
|
pub fn ptr(&self) -> NonNull<ffi::AAssetDir> {
|
|
self.ptr
|
|
}
|
|
|
|
/// Get the next filename, if any, and process it. Like [`Iterator::next()`], but performs
|
|
/// no additional allocation.
|
|
///
|
|
/// The filenames are in the correct format to be passed to [`AssetManager::open()`].
|
|
#[doc(alias = "AAssetDir_getNextFileName")]
|
|
pub fn with_next<T>(&mut self, f: impl for<'a> FnOnce(&'a CStr) -> T) -> Option<T> {
|
|
unsafe {
|
|
let next_name = ffi::AAssetDir_getNextFileName(self.ptr.as_ptr());
|
|
if next_name.is_null() {
|
|
None
|
|
} else {
|
|
Some(f(CStr::from_ptr(next_name)))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Reset the iteration state
|
|
#[doc(alias = "AAssetDir_rewind")]
|
|
pub fn rewind(&mut self) {
|
|
unsafe {
|
|
ffi::AAssetDir_rewind(self.ptr.as_ptr());
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Iterator for AssetDir {
|
|
type Item = CString;
|
|
|
|
fn next(&mut self) -> Option<CString> {
|
|
self.with_next(|cstr| cstr.to_owned())
|
|
}
|
|
}
|
|
|
|
/// A native [`AAsset *`], opened in streaming mode
|
|
///
|
|
/// ```no_run
|
|
/// # use std::ffi::CString;
|
|
/// # use ndk::asset::AssetManager;
|
|
/// # let asset_manager: AssetManager = unimplemented!();
|
|
/// use std::io::Read;
|
|
///
|
|
/// let asset = asset_manager
|
|
/// .open(&CString::new("path/to/asset").unwrap())
|
|
/// .expect("Could not open asset");
|
|
///
|
|
/// let mut data = vec![];
|
|
/// asset.read_to_end(&mut data);
|
|
/// // ... use data ...
|
|
/// ```
|
|
///
|
|
/// [`AAsset *`]: https://developer.android.com/ndk/reference/group/asset#aasset
|
|
#[derive(Debug)]
|
|
#[doc(alias = "AAsset")]
|
|
pub struct Asset {
|
|
ptr: NonNull<ffi::AAsset>,
|
|
}
|
|
|
|
// AAsset is *not* thread safe.
|
|
// See https://developer.android.com/ndk/reference/group/asset#aasset
|
|
|
|
impl Drop for Asset {
|
|
#[doc(alias = "AAsset_close")]
|
|
fn drop(&mut self) {
|
|
unsafe { ffi::AAsset_close(self.ptr.as_ptr()) }
|
|
}
|
|
}
|
|
|
|
impl Asset {
|
|
/// Construct an `Asset` from the native `AAsset *`. This gives ownership of the `AAsset *` to
|
|
/// the `Asset`, which will handle closing the asset. Avoid using the pointer after calling
|
|
/// this function.
|
|
///
|
|
/// # Safety
|
|
/// By calling this function, you assert that it points to a valid native `AAsset`, open
|
|
/// in the streaming mode.
|
|
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAsset>) -> Self {
|
|
Self { ptr }
|
|
}
|
|
|
|
/// The corresponding native `AAsset *`
|
|
pub fn ptr(&self) -> NonNull<ffi::AAsset> {
|
|
self.ptr
|
|
}
|
|
|
|
/// Returns the total length of the asset, in bytes
|
|
#[doc(alias = "AAsset_getLength64")]
|
|
pub fn length(&self) -> usize {
|
|
unsafe { ffi::AAsset_getLength64(self.ptr.as_ptr()) as usize }
|
|
}
|
|
|
|
/// Returns the remaining length of the asset, in bytes
|
|
#[doc(alias = "AAsset_getRemainingLength64")]
|
|
pub fn remaining_length(&self) -> usize {
|
|
unsafe { ffi::AAsset_getRemainingLength64(self.ptr.as_ptr()) as usize }
|
|
}
|
|
|
|
/// Maps all data into a buffer and returns it
|
|
#[doc(alias = "AAsset_getBuffer")]
|
|
pub fn buffer(&mut self) -> io::Result<&[u8]> {
|
|
unsafe {
|
|
let buf_ptr = ffi::AAsset_getBuffer(self.ptr.as_ptr());
|
|
if buf_ptr.is_null() {
|
|
Err(io::Error::new(
|
|
io::ErrorKind::Other,
|
|
"Android Asset error creating buffer",
|
|
))
|
|
} else {
|
|
Ok(std::slice::from_raw_parts(
|
|
buf_ptr as *const u8,
|
|
self.length(),
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns whether this asset's internal buffer is allocated in ordinary RAM (i.e. not `mmap`ped).
|
|
#[doc(alias = "AAsset_isAllocated")]
|
|
pub fn is_allocated(&self) -> bool {
|
|
unsafe { ffi::AAsset_isAllocated(self.ptr.as_ptr()) != 0 }
|
|
}
|
|
|
|
/// Open a new file descriptor that can be used to read the asset data.
|
|
///
|
|
/// Returns an error if direct fd access is not possible (for example, if the asset is compressed).
|
|
#[doc(alias = "AAsset_openFileDescriptor64")]
|
|
pub fn open_file_descriptor(&self) -> io::Result<OpenedFileDescriptor> {
|
|
let mut offset = 0;
|
|
let mut size = 0;
|
|
let res =
|
|
unsafe { ffi::AAsset_openFileDescriptor64(self.ptr.as_ptr(), &mut offset, &mut size) };
|
|
if res >= 0 {
|
|
Ok(OpenedFileDescriptor {
|
|
fd: unsafe { OwnedFd::from_raw_fd(res) },
|
|
offset: offset as usize,
|
|
size: size as usize,
|
|
})
|
|
} else {
|
|
Err(io::Error::new(
|
|
io::ErrorKind::Other,
|
|
"Android Asset openFileDescriptor error",
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl io::Read for Asset {
|
|
#[doc(alias = "AAsset_read")]
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
unsafe {
|
|
let res = ffi::AAsset_read(self.ptr.as_ptr(), buf.as_mut_ptr() as *mut _, buf.len());
|
|
if res >= 0 {
|
|
Ok(res as usize)
|
|
} else {
|
|
Err(io::Error::new(
|
|
io::ErrorKind::Other,
|
|
"Android Asset read error",
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl io::Seek for Asset {
|
|
#[doc(alias = "AAsset_seek64")]
|
|
fn seek(&mut self, seek: io::SeekFrom) -> io::Result<u64> {
|
|
unsafe {
|
|
let res = match seek {
|
|
io::SeekFrom::Start(x) => {
|
|
ffi::AAsset_seek64(self.ptr.as_ptr(), x as i64, ffi::SEEK_SET as i32)
|
|
}
|
|
io::SeekFrom::Current(x) => {
|
|
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_CUR as i32)
|
|
}
|
|
io::SeekFrom::End(x) => {
|
|
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_END as i32)
|
|
}
|
|
};
|
|
if res < 0 {
|
|
Err(io::Error::new(
|
|
io::ErrorKind::Other,
|
|
"Android Asset seek error",
|
|
))
|
|
} else {
|
|
Ok(res as u64)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Contains the opened file descriptor returned by [`Asset::open_file_descriptor()`], together
|
|
/// with the offset and size of the given asset within that file descriptor.
|
|
#[derive(Debug)]
|
|
pub struct OpenedFileDescriptor {
|
|
pub fd: OwnedFd,
|
|
pub offset: usize,
|
|
pub size: usize,
|
|
}
|