ark-api 0.17.0-pre.15

Ark API
Documentation
use super::*;
pub use ffi::RetrieveDataType;

/// Represents a data object.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct DataHandle(pub ffi::DataHandle);

#[allow(missing_docs)]
impl DataHandle {
    #[inline]
    pub fn from_ffi(h: ffi::DataHandle) -> Self {
        Self(h)
    }

    #[inline]
    pub fn as_ffi(self) -> ffi::DataHandle {
        self.0
    }

    /// NOTE: This does not check whether there's actually a data object represented by this
    /// data handle. For that, use `World::is_valid_data`.
    #[inline]
    pub fn is_valid(self) -> bool {
        (self.0 & 0xFFFF_FFFF) != 0xFFFF_FFFF
    }

    /// Returns an eternally invalid mesh handle.
    #[inline]
    pub fn invalid() -> Self {
        Self(0x0000_0001_FFFF_FFFF)
    }

    pub fn try_from_ffi(h: ffi::DataHandle) -> Option<Self> {
        let eh = Self::from_ffi(h);
        eh.is_valid().then_some(eh)
    }

    pub fn try_to_ffi(self) -> Option<u64> {
        self.is_valid().then(|| self.as_ffi())
    }

    pub fn read_from<T: Sized>(self, retrieve_data_type: ffi::RetrieveDataType) -> T {
        let len = World::retrieve_data(self, retrieve_data_type, &mut []);

        let elem_size = std::mem::size_of::<T>() as u32;
        assert!(len == elem_size);

        let mut data: T = unsafe { std::mem::zeroed::<T>() };

        let data_slice = unsafe {
            std::slice::from_raw_parts_mut(((&mut data) as *mut T).cast::<u8>(), len as usize)
        };
        let new_len = World::retrieve_data(self, retrieve_data_type, data_slice);
        assert!(new_len == len);

        data
    }
    pub fn read<T: Sized>(self) -> T {
        self.read_from(ffi::RetrieveDataType::Output)
    }

    pub fn read_str_from(self, retrieve_data_type: ffi::RetrieveDataType) -> String {
        std::str::from_utf8(&World::retrieve_data_vec(self, retrieve_data_type))
            .unwrap_or_default()
            .to_owned()
    }

    pub fn read_str(self) -> String {
        self.read_str_from(ffi::RetrieveDataType::Output)
    }

    pub fn read_vec_from<T: Sized + Clone>(
        self,
        retrieve_data_type: ffi::RetrieveDataType,
    ) -> Vec<T> {
        let len = World::retrieve_data(self, retrieve_data_type, &mut []);

        let elem_size = std::mem::size_of::<T>() as u32;
        assert!((len % elem_size) == 0);
        let num_elements = len / elem_size;

        let mut data: Vec<T> = vec![unsafe { std::mem::zeroed::<T>() }; num_elements as usize];

        let data_slice =
            unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr().cast::<u8>(), len as usize) };
        let new_len = World::retrieve_data(self, retrieve_data_type, data_slice);
        assert!(new_len == len);

        data
    }

    pub fn read_vec<T: Sized + Clone>(self) -> Vec<T> {
        self.read_vec_from(ffi::RetrieveDataType::Output)
    }
}

impl DataHandle {}

/// Helper struct that represents a data object.

#[derive(Debug, Eq, PartialEq, Hash)]
pub struct WorldData {
    id: DataHandle,
}

impl Drop for WorldData {
    fn drop(&mut self) {
        World::destroy_data(self.id);
    }
}

impl Clone for WorldData {
    fn clone(&self) -> Self {
        World::retain_data(self.id);
        Self { id: self.id }
    }
}

impl WorldData {
    /// Creates a `WorldData` from the passed-in input data.
    pub fn create(create_data_type: ffi::CreateDataType, input_data: &[u8]) -> Self {
        Self {
            id: World::create_data(create_data_type, input_data),
        }
    }

    /// Creates a `WorldData` from the passed-in input data.
    pub fn create_struct<T: Sized>(create_data_type: ffi::CreateDataType, input_data: &T) -> Self {
        let data_slice = unsafe {
            std::slice::from_raw_parts(
                (input_data as *const T).cast::<u8>(),
                std::mem::size_of::<T>(),
            )
        };

        Self {
            id: World::create_data(create_data_type, data_slice),
        }
    }

    /// Creates a `FormattedText` object from the passed-in string.
    pub fn create_formatted_text(input_data: &str) -> Self {
        Self {
            id: World::create_data(ffi::CreateDataType::FormattedText, input_data.as_bytes()),
        }
    }

    /// Creates a `RenderModuleData` object from the passed-in module dependency name and data.
    pub fn create_render_module_data(module_dependency_name: &str, input_data: &[u8]) -> Self {
        let raw_data = ffi::RawRenderModuleData {
            module_dependency_name_ptr: module_dependency_name.as_ptr() as u32,
            module_dependency_name_len: module_dependency_name.len() as u32,
            static_data_ptr: input_data.as_ptr() as u32,
            static_data_len: input_data.len() as u32,
        };
        Self::create_struct(ffi::CreateDataType::RenderModule, &raw_data)
    }

    /// Creates a `AudioModuleData` object from the passed-in module dependency name and data.
    pub fn create_audio_module_data(module_dependency_name: &str, input_data: &[u8]) -> Self {
        let raw_data = ffi::RawAudioModuleData {
            module_dependency_name_ptr: module_dependency_name.as_ptr() as u32,
            module_dependency_name_len: module_dependency_name.len() as u32,
            data_ptr: input_data.as_ptr() as u32,
            data_len: input_data.len() as u32,
        };
        Self::create_struct(ffi::CreateDataType::AudioModule, &raw_data)
    }

    /// Gets different data from this data object.
    pub fn retrieve_data<T: Sized>(&self, t: ffi::RetrieveDataType) -> T {
        self.id.read_from::<T>(t)
    }

    /// Retrieves the internal data type of the `WorldData` instance.
    pub fn data_type(&self) -> ffi::WorldDataType {
        self.retrieve_data(ffi::RetrieveDataType::WorldDataType)
    }

    /// Gets the `DataHandle` of this data object.
    #[inline]
    pub fn get_data_handle(&self) -> DataHandle {
        self.id
    }

    /// Adopts a raw data handle and increases its ref count.
    pub fn from_data_handle(handle: DataHandle) -> Self {
        World::retain_data(handle);
        Self { id: handle }
    }

    /// Sets a debug name of this data object. Useful for debugging memory usage and leaks.
    pub fn set_debug_name(&self, name: &str) {
        World::set_data_debug_name(self.id, name);
    }

    /// Gets a debug name of this data object.
    pub fn debug_name(&self) -> String {
        World::get_data_debug_name(self.id)
    }

    /// Returns `true` if this is a valid data object. This should always be the case unless
    /// something has gone very wrong, such as the case where a user created this object from
    // an invalid handle).
    #[inline]
    pub fn is_valid(&self) -> bool {
        World::is_valid_data(self.id)
    }
}

impl ValueConverterTrait<WorldData> for ValueConverter {
    fn into_value(v: WorldData) -> Value {
        // As we move the WorldData here we can see it as moving
        // it with all the references into the DataHandle. Thus we don't
        // want to call drop here to release the reference count.
        let v = std::mem::ManuallyDrop::new(v);
        let handle = v.get_data_handle();
        <Self as ValueConverterTrait<DataHandle>>::into_value(handle)
    }
    fn from_value(v: &Value) -> WorldData {
        // Create a WorldData object from the data handle and retain its reference count.
        WorldData::from_data_handle(<Self as ValueConverterTrait<DataHandle>>::from_value(v))
    }
}

impl ValueConverterTrait<DataHandle> for ValueConverter {
    fn into_value(v: DataHandle) -> Value {
        Value::from_i64(v.0 as i64)
    }
    fn from_value(v: &Value) -> DataHandle {
        DataHandle::from_ffi(v.as_i64() as u64)
    }
}

/// Represents raw binary data
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct BinaryData {
    pub(crate) data: WorldData,
}

impl BinaryData {
    /// Creates the `BinaryData` from `data`.
    ///
    pub fn new(data: &[u8]) -> Self {
        Self {
            data: WorldData::create(ffi::CreateDataType::Binary, data),
        }
    }
}

impl ValueConverterTrait<BinaryData> for ValueConverter {
    fn into_value(v: BinaryData) -> Value {
        <Self as ValueConverterTrait<WorldData>>::into_value(v.data)
    }
    fn from_value(v: &Value) -> BinaryData {
        BinaryData {
            data: <Self as ValueConverterTrait<WorldData>>::from_value(v),
        }
    }
}