diff --git a/src/ffi/dynamic_data/models/battle.rs b/src/ffi/dynamic_data/models/battle.rs index cae3390..fe178ed 100644 --- a/src/ffi/dynamic_data/models/battle.rs +++ b/src/ffi/dynamic_data/models/battle.rs @@ -3,7 +3,7 @@ use crate::dynamic_data::{ }; use crate::ffi::dynamic_data::models::native_event_hook::NativeEventHook; use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; -use crate::ffi::FFIResult; +use crate::ffi::{FFIResult, OwnedPtrString}; use anyhow::anyhow; use std::ffi::{c_char, CStr, CString}; use std::ops::Deref; @@ -204,13 +204,13 @@ extern "C" fn battle_set_weather(ptr: FFIHandle, weather: *const c_char) /// Gets the current weather of the battle. If no weather is present, this returns nullptr. #[no_mangle] -extern "C" fn battle_weather_name(ptr: FFIHandle) -> FFIResult<*mut c_char> { +extern "C" fn battle_weather_name(ptr: FFIHandle) -> FFIResult { match ptr.from_ffi_handle().weather_name() { Ok(Some(w)) => match CString::new(w.str()) { - Ok(s) => s.into_raw().into(), + Ok(s) => OwnedPtrString(s.into_raw()).into(), Err(e) => FFIResult::err(anyhow!("Failed to convert weather name to CString: {}", e)), }, - Ok(None) => std::ptr::null_mut::().into(), + Ok(None) => OwnedPtrString(std::ptr::null_mut()).into(), Err(e) => FFIResult::err(e), } } diff --git a/src/ffi/dynamic_data/models/pokemon.rs b/src/ffi/dynamic_data/models/pokemon.rs index 830b961..4d254a3 100644 --- a/src/ffi/dynamic_data/models/pokemon.rs +++ b/src/ffi/dynamic_data/models/pokemon.rs @@ -1,7 +1,7 @@ use crate::defines::LevelInt; use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, LearnedMove, MoveLearnMethod, Pokemon}; use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; -use crate::ffi::FFIResult; +use crate::ffi::{FFIResult, OwnedPtrString}; use crate::static_data::{ Ability, AbilityIndex, Form, Gender, Item, Nature, Species, Statistic, StatisticSet, TypeIdentifier, }; @@ -180,16 +180,16 @@ extern "C" fn pokemon_height(handle: FFIHandle) -> f32 { /// An optional nickname of the Pokemon. #[no_mangle] -extern "C" fn pokemon_nickname(handle: FFIHandle) -> FFIResult<*mut c_char> { +extern "C" fn pokemon_nickname(handle: FFIHandle) -> FFIResult { let form = handle.from_ffi_handle(); let name = form.nickname(); if let Some(v) = name { match CString::new(v.as_str()) { - Ok(v) => FFIResult::ok(v.into_raw()), + Ok(v) => FFIResult::ok(OwnedPtrString(v.into_raw())), Err(err) => FFIResult::err(anyhow!("Could not convert nickname to CString: {}", err)), } } else { - FFIResult::ok(std::ptr::null_mut()) + FFIResult::ok(OwnedPtrString(std::ptr::null_mut())) } } diff --git a/src/ffi/ffi_handle.rs b/src/ffi/ffi_handle.rs index d05596a..ec19b88 100644 --- a/src/ffi/ffi_handle.rs +++ b/src/ffi/ffi_handle.rs @@ -45,6 +45,15 @@ impl Clone for FFIHandle { impl Copy for FFIHandle {} +impl Default for FFIHandle { + fn default() -> Self { + Self { + handle: 0, + _marker: std::marker::PhantomData, + } + } +} + /// An FFIObject we can fetch from the FFIHandle. We store this in a hashmap to be able to fetch /// the object from the handle, and to be able to drop the object when the FFI is done with the handle #[derive(Clone)] @@ -98,7 +107,7 @@ unsafe impl Send for FFIObject {} unsafe impl Sync for FFIObject {} /// The next handle to be used. -static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(0); +static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(1); /// A lookup to get an actual object from a handle. static FFI_OBJECTS: LazyLock>> = LazyLock::new(|| RwLock::new(HashMap::new())); /// A lookup to get a handle from an object. @@ -137,7 +146,7 @@ impl FFIHandle { FFI_OBJECTS .read() .get(&self.handle) - .ok_or(anyhow!("Unable to get handle")) + .ok_or(anyhow!("Unable to get handle {} from FFI_OBJECTS", self.handle)) .unwrap() .clone() } diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 9c26636..b26ed15 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -8,10 +8,19 @@ mod static_data; pub(self) use ffi_handle::*; /// Helper type for clearer functions. -type OwnedPtrString = *mut c_char; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +struct OwnedPtrString(*mut c_char); + /// Helper type for clearer functions. type NonOwnedPtrString = *const c_char; +impl Default for OwnedPtrString { + fn default() -> Self { + OwnedPtrString(std::ptr::null_mut()) + } +} + /// Generates a basic getter foreign function interface. macro_rules! ffi_handle_arc_dyn_getter { ( @@ -71,7 +80,7 @@ macro_rules! ffi_handle_vec_stringkey_getters { } #[no_mangle] extern "C" fn [< $type:lower _ $func _get>](ptr: FFIHandle>, index: usize) -> OwnedPtrString { - CString::new(ptr.from_ffi_handle().$func().get(index).unwrap().str()).unwrap().into_raw() + OwnedPtrString(CString::new(ptr.from_ffi_handle().$func().get(index).unwrap().str()).unwrap().into_raw()) } } }; @@ -102,40 +111,30 @@ impl ExternPointer { } } -/// Helper utility class to give either the data or an error to a FFI. -#[repr(C)] -union ResultUnion { - /// If the result is ok, this contains the value. - ok: T, - /// If the result is an error, this contains the error message. - err: NonOwnedPtrString, -} - /// The result of a FFI call that can either be an error or a value. #[repr(C)] -pub struct FFIResult { - /// If the result is ok, this is 1, otherwise 0. - ok: u8, +#[repr(packed)] +pub struct FFIResult { + /// If the result is ok, this is null, otherwise this is a pointer to the error string. + err: NonOwnedPtrString, /// The value or error. - value: ResultUnion, + value: T, } -impl FFIResult { +impl FFIResult { /// Creates a new NativeResult with the given value. pub fn ok(value: T) -> Self { Self { - ok: 1, - value: ResultUnion { ok: value }, + err: std::ptr::null(), + value, } } /// Creates a new NativeResult with the given error. #[allow(clippy::unwrap_used)] // We know for certain this is not empty. pub fn err(err: anyhow_ext::Error) -> Self { Self { - ok: 0, - value: ResultUnion { - err: CString::new(err.to_string()).unwrap().into_raw(), - }, + err: CString::new(err.to_string()).unwrap().into_raw(), + value: Default::default(), } } @@ -143,15 +142,13 @@ impl FFIResult { #[allow(clippy::unwrap_used)] // We know for certain this is not empty. pub fn err_from_str(err: &str) -> Self { Self { - ok: 0, - value: ResultUnion { - err: CString::new(err).unwrap().into_raw(), - }, + err: CString::new(err).unwrap().into_raw(), + value: Default::default(), } } } -impl From> for FFIResult { +impl From> for FFIResult { fn from(value: anyhow::Result) -> Self { match value { Ok(v) => Self::ok(v), @@ -160,7 +157,7 @@ impl From> for FFIResult { } } -impl From for FFIResult { +impl From for FFIResult { fn from(value: T) -> Self { Self::ok(value) } diff --git a/src/ffi/static_data/ability.rs b/src/ffi/static_data/ability.rs index 43877dc..8879955 100644 --- a/src/ffi/static_data/ability.rs +++ b/src/ffi/static_data/ability.rs @@ -38,7 +38,7 @@ unsafe extern "C" fn ability_new( #[no_mangle] unsafe extern "C" fn ability_name(handle: FFIHandle>) -> FFIResult { match CString::new(handle.from_ffi_handle().name().str()) { - Ok(s) => FFIResult::ok(s.into_raw()), + Ok(s) => FFIResult::ok(OwnedPtrString(s.into_raw())), Err(_) => FFIResult::err(anyhow!("Failed to convert name to CString")), } } @@ -47,7 +47,7 @@ unsafe extern "C" fn ability_name(handle: FFIHandle>) -> FFIRes #[no_mangle] unsafe extern "C" fn ability_effect(ptr: FFIHandle>) -> FFIResult { match CString::new(ptr.from_ffi_handle().effect().str()) { - Ok(s) => FFIResult::ok(s.into_raw()), + Ok(s) => FFIResult::ok(OwnedPtrString(s.into_raw())), Err(_) => FFIResult::err(anyhow!("Failed to convert effect to CString")), } } diff --git a/src/ffi/static_data/form.rs b/src/ffi/static_data/form.rs index c9911e9..76076a1 100644 --- a/src/ffi/static_data/form.rs +++ b/src/ffi/static_data/form.rs @@ -75,7 +75,7 @@ unsafe extern "C" fn form_name(ptr: FFIHandle>) -> FFIResult FFIResult::ok(name.into_raw()), + Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())), Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())), } } diff --git a/src/ffi/static_data/item.rs b/src/ffi/static_data/item.rs index 109f874..b5638d8 100644 --- a/src/ffi/static_data/item.rs +++ b/src/ffi/static_data/item.rs @@ -41,7 +41,7 @@ unsafe extern "C" fn item_name(ptr: FFIHandle>) -> FFIResult FFIResult::ok(name.into_raw()), + Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())), Err(_) => FFIResult::err(anyhow!("Unable to convert name `{}` to CString", name.str())), } } diff --git a/src/ffi/static_data/libraries/mod.rs b/src/ffi/static_data/libraries/mod.rs index cc80160..346f0a3 100644 --- a/src/ffi/static_data/libraries/mod.rs +++ b/src/ffi/static_data/libraries/mod.rs @@ -53,9 +53,9 @@ macro_rules! library_interface { let lib = ptr.from_ffi_handle(); let v = lib.get_key_by_index(index); if let Some(value) = v { - std::ffi::CString::new(value.str()).unwrap().into_raw() + OwnedPtrString(std::ffi::CString::new(value.str()).unwrap().into_raw()) } else { - std::ptr::null_mut() + OwnedPtrString(std::ptr::null_mut()) } } diff --git a/src/ffi/static_data/libraries/nature_library.rs b/src/ffi/static_data/libraries/nature_library.rs index abd060d..ea30ad4 100644 --- a/src/ffi/static_data/libraries/nature_library.rs +++ b/src/ffi/static_data/libraries/nature_library.rs @@ -68,7 +68,7 @@ unsafe extern "C" fn nature_library_get_nature_name( let name = ptr.from_ffi_handle().get_nature_name(&nature); match name { Ok(name) => match CString::new(name.str()) { - Ok(cstr) => FFIResult::ok(cstr.into_raw()), + Ok(cstr) => FFIResult::ok(OwnedPtrString(cstr.into_raw())), Err(_) => FFIResult::err(anyhow!("Failed to convert nature name to C string")), }, Err(e) => FFIResult::err(e), diff --git a/src/ffi/static_data/libraries/type_library.rs b/src/ffi/static_data/libraries/type_library.rs index a72c70f..b7b59ed 100644 --- a/src/ffi/static_data/libraries/type_library.rs +++ b/src/ffi/static_data/libraries/type_library.rs @@ -1,7 +1,7 @@ use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; -use crate::ffi::{FFIResult, NonOwnedPtrString}; +use crate::ffi::{FFIResult, NonOwnedPtrString, OwnedPtrString}; use crate::static_data::{TypeIdentifier, TypeLibrary, TypeLibraryImpl}; -use std::ffi::{c_char, CStr, CString}; +use std::ffi::{CStr, CString}; use std::sync::Arc; /// Instantiates a new type library with a specific capacity. @@ -33,17 +33,17 @@ unsafe extern "C" fn type_library_get_type_name( ptr: FFIHandle>, type_id: TypeIdentifier, found: *mut bool, -) -> FFIResult<*mut c_char> { +) -> FFIResult { if let Some(v) = ptr.from_ffi_handle().get_type_name(type_id) { *found = true; match CString::new(v.str()) { - Ok(v) => FFIResult::ok(v.into_raw()), + Ok(v) => FFIResult::ok(OwnedPtrString(v.into_raw())), Err(e) => FFIResult::err(e.into()), } } else { *found = false; - FFIResult::ok(std::ptr::null_mut()) + FFIResult::ok(OwnedPtrString(std::ptr::null_mut())) } } diff --git a/src/ffi/static_data/mod.rs b/src/ffi/static_data/mod.rs index 5c990db..88c89c1 100644 --- a/src/ffi/static_data/mod.rs +++ b/src/ffi/static_data/mod.rs @@ -108,7 +108,7 @@ extern "C" fn effect_parameter_get_as_string(ptr: FFIHandle let p = ptr.from_ffi_handle(); if let EffectParameter::String(b) = p.deref() { match CString::new(b.str().to_string()) { - Ok(cstr) => FFIResult::ok(cstr.into_raw()), + Ok(cstr) => FFIResult::ok(OwnedPtrString(cstr.into_raw())), Err(_) => FFIResult::err(PkmnError::InvalidCString.into()), } } else { diff --git a/src/ffi/static_data/move_data.rs b/src/ffi/static_data/move_data.rs index 90b6615..55e949b 100644 --- a/src/ffi/static_data/move_data.rs +++ b/src/ffi/static_data/move_data.rs @@ -1,5 +1,5 @@ use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; -use crate::ffi::{ffi_handle_arc_dyn_getter, ExternPointer, FFIResult, NonOwnedPtrString, OwnedPtrString}; +use crate::ffi::{ffi_handle_arc_dyn_getter, FFIResult, NonOwnedPtrString, OwnedPtrString}; use crate::static_data::{ EffectParameter, MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffect, SecondaryEffectImpl, TypeIdentifier, @@ -64,7 +64,7 @@ unsafe extern "C" fn move_data_name(ptr: FFIHandle>) -> FFIRes let move_data = ptr.from_ffi_handle(); let name = move_data.name(); match CString::new(name.str()) { - Ok(name) => FFIResult::ok(name.into_raw()), + Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())), Err(_) => FFIResult::err_from_str("Unable to convert name to string"), } } @@ -90,9 +90,9 @@ unsafe extern "C" fn move_data_secondary_effect( /// Arbitrary flags that can be applied to the move. #[no_mangle] -unsafe extern "C" fn move_data_has_flag(ptr: ExternPointer>, flag: *const c_char) -> u8 { +unsafe extern "C" fn move_data_has_flag(ptr: FFIHandle>, flag: *const c_char) -> u8 { let flag = CStr::from_ptr(flag).into(); - u8::from(ptr.as_ref().has_flag(&flag)) + u8::from(ptr.from_ffi_handle().has_flag(&flag)) } /// Instantiates a new Secondary Effect. @@ -119,37 +119,37 @@ unsafe extern "C" fn secondary_effect_new( /// The chance the effect triggers. #[no_mangle] -unsafe extern "C" fn secondary_effect_chance(ptr: ExternPointer>) -> f32 { - ptr.as_ref().chance() +unsafe extern "C" fn secondary_effect_chance(ptr: FFIHandle>) -> f32 { + ptr.from_ffi_handle().chance() } /// The name of the effect. #[no_mangle] unsafe extern "C" fn secondary_effect_effect_name( - ptr: ExternPointer>, + ptr: FFIHandle>, ) -> FFIResult { - match CString::new(ptr.as_ref().effect_name().str()) { - Ok(name) => FFIResult::ok(name.into_raw()), + match CString::new(ptr.from_ffi_handle().effect_name().str()) { + Ok(name) => FFIResult::ok(OwnedPtrString(name.into_raw())), Err(_) => FFIResult::err(anyhow!( "Unable to convert effect name '{}' to CString", - ptr.as_ref().effect_name() + ptr.from_ffi_handle().effect_name() )), } } /// The length of parameters of the effect. #[no_mangle] -unsafe extern "C" fn secondary_effect_parameter_length(ptr: ExternPointer>) -> usize { - ptr.as_ref().parameters().len() +unsafe extern "C" fn secondary_effect_parameter_length(ptr: FFIHandle>) -> usize { + ptr.from_ffi_handle().parameters().len() } /// Get a parameter of the effect. #[no_mangle] unsafe extern "C" fn secondary_effect_parameter_get( - ptr: ExternPointer>, + ptr: FFIHandle>, index: usize, ) -> FFIHandle> { - if let Some(v) = ptr.as_ref().parameters().get(index) { + if let Some(v) = ptr.from_ffi_handle().parameters().get(index) { FFIHandle::get_handle(v.clone().into()) } else { FFIHandle::none()