diff --git a/Cargo.toml b/Cargo.toml index 5c1a8ef..a11e605 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,21 +8,21 @@ edition = "2018" [lib] name = "pkmn_lib" -crate_type = ["rlib"] +crate_type = ["cdylib", "rlib"] path = "src/lib.rs" [features] ffi = [] serde = ["dep:serde"] wasm = ["dep:wasmer", "dep:unique-type-id", "dep:unique-type-id-derive"] -default = ["serde", "wasm"] +default = ["serde", "wasm", "ffi"] [profile.dev] opt-level = 0 debug = true debug-assertions = true overflow-checks = true -lto = "thin" +lto = false panic = 'unwind' incremental = true codegen-units = 256 @@ -33,7 +33,7 @@ opt-level = 3 debug = 1 debug-assertions = false overflow-checks = true -lto = "fat" +lto = false panic = 'unwind' incremental = false codegen-units = 16 @@ -56,7 +56,7 @@ wasmer = { version = "3.0.0-beta", optional = true, default-features = true } unique-type-id = { version = "1.0.0", optional = true } unique-type-id-derive = { version = "1.0.0", optional = true } paste = { version = "1.0.8" } -arcstr = "1.1.4" +arcstr = { version = "1.1.4", features = ["std"] } [dev-dependencies] csv = "1.1.6" diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs new file mode 100644 index 0000000..a1449f1 --- /dev/null +++ b/src/ffi/mod.rs @@ -0,0 +1,100 @@ +mod static_data; + +type OwnedPtr = *mut T; +type BorrowedPtr = *const T; + +macro_rules! ffi_getter { + ( + $type:ty, $func:ident, $returns: ty + ) => { + paste::paste! { + #[no_mangle] + extern "C" fn [< $type:snake _ $func >](ptr: ExternPointer<$type>) -> $returns { + ptr.as_ref().$func() + } + } + }; +} + +macro_rules! ffi_stringkey_getter { + ( + $type:ty, $func:ident + ) => { + paste::paste! { + #[no_mangle] + extern "C" fn [< $type:lower _ $func >](ptr: ExternPointer<$type>) -> OwnedPtr { + std::ffi::CString::new(ptr.as_ref().$func().str()).unwrap().into_raw() + } + } + }; +} + +macro_rules! ffi_vec_value_getters { + ( + $type:ty, $func:ident, $returns: ty + ) => { + paste::paste! { + #[no_mangle] + extern "C" fn [< $type:lower _ $func _length>](ptr: ExternPointer<$type>) -> usize { + ptr.as_ref().$func().len() + } + #[no_mangle] + extern "C" fn [< $type:lower _ $func _get>](ptr: ExternPointer<$type>, index: usize) -> $returns { + *ptr.as_ref().$func().get(index).unwrap() + } + } + }; +} + +macro_rules! ffi_vec_stringkey_getters { + ( + $type:ident, $func:ident + ) => { + paste::paste! { + #[no_mangle] + extern "C" fn [< $type:lower _ $func _length>](ptr: ExternPointer<$type>) -> usize { + ptr.as_ref().$func().len() + } + #[no_mangle] + extern "C" fn [< $type:lower _ $func _get>](ptr: ExternPointer<$type>, index: usize) -> OwnedPtr { + CString::new(ptr.as_ref().$func().get(index).unwrap().str()).unwrap().into_raw() + } + } + }; +} + +pub(self) use ffi_getter; +pub(self) use ffi_stringkey_getter; +pub(self) use ffi_vec_stringkey_getters; +pub(self) use ffi_vec_value_getters; + +#[repr(C)] +pub(self) struct ExternPointer { + ptr: *mut T, +} + +impl ExternPointer { + pub(self) fn as_ref(&self) -> &T { + unsafe { + self.ptr.as_ref().expect(&format!( + "Given pointer of type '{}' was null", + std::any::type_name::() + )) + } + } + + pub(self) fn as_mut(&self) -> &mut T { + unsafe { + self.ptr.as_mut().expect(&format!( + "Given pointer of type '{}' was null", + std::any::type_name::() + )) + } + } +} + +impl Into> for *mut T { + fn into(self) -> ExternPointer { + ExternPointer { ptr: self } + } +} diff --git a/src/ffi/static_data/ability.rs b/src/ffi/static_data/ability.rs new file mode 100644 index 0000000..40e7639 --- /dev/null +++ b/src/ffi/static_data/ability.rs @@ -0,0 +1,53 @@ +use crate::ffi::{BorrowedPtr, ExternPointer, OwnedPtr}; +use crate::static_data::{Ability, EffectParameter}; +use crate::StringKey; +use std::ffi::{c_char, CStr, CString}; +use std::ptr::drop_in_place; + +#[no_mangle] +unsafe extern "C" fn ability_new( + name: *const c_char, + effect: *const c_char, + parameters: *const OwnedPtr, + parameters_length: usize, +) -> OwnedPtr { + let parameters = std::slice::from_raw_parts(parameters, parameters_length); + let mut parameters_vec: Vec = Vec::with_capacity(parameters_length); + for parameter in parameters { + parameters_vec.push(*Box::from_raw(*parameter)); + } + + let name: StringKey = CStr::from_ptr(name).to_str().unwrap().into(); + let effect: StringKey = CStr::from_ptr(effect).to_str().unwrap().into(); + + Box::into_raw(Box::new(Ability::new(&name, &effect, parameters_vec))) +} + +#[no_mangle] +unsafe extern "C" fn ability_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +#[no_mangle] +unsafe extern "C" fn ability_name(ptr: ExternPointer) -> OwnedPtr { + CString::new(ptr.as_ref().name().str()).unwrap().into_raw() +} + +#[no_mangle] +unsafe extern "C" fn ability_effect(ptr: ExternPointer) -> OwnedPtr { + CString::new(ptr.as_ref().effect().str()).unwrap().into_raw() +} + +#[no_mangle] +unsafe extern "C" fn ability_parameter_length(ptr: ExternPointer) -> usize { + ptr.as_ref().parameters().len() +} + +#[no_mangle] +unsafe extern "C" fn ability_parameter_get(ptr: ExternPointer, index: usize) -> BorrowedPtr { + if let Some(p) = ptr.as_ref().parameters().get(index) { + p as *const EffectParameter + } else { + std::ptr::null() + } +} diff --git a/src/ffi/static_data/form.rs b/src/ffi/static_data/form.rs new file mode 100644 index 0000000..771145c --- /dev/null +++ b/src/ffi/static_data/form.rs @@ -0,0 +1,90 @@ +use crate::ffi::{ffi_getter, ffi_vec_stringkey_getters, ffi_vec_value_getters, BorrowedPtr, ExternPointer, OwnedPtr}; +use crate::static_data::{Form, LearnableMoves, StaticStatisticSet, TypeIdentifier}; +use crate::StringKey; +use hashbrown::HashSet; +use std::ffi::{c_char, CStr, CString}; +use std::ptr::drop_in_place; + +#[no_mangle] +unsafe extern "C" fn form_new( + name: *const c_char, + height: f32, + weight: f32, + base_experience: u32, + types: *const TypeIdentifier, + types_length: usize, + base_stats: OwnedPtr>, + abilities: *const BorrowedPtr, + abilities_length: usize, + hidden_abilities: *const BorrowedPtr, + hidden_abilities_length: usize, + moves: OwnedPtr, + flags: *const *const c_char, + flags_length: usize, +) -> OwnedPtr
{ + let name: StringKey = CStr::from_ptr(name).to_str().unwrap().into(); + + let abilities = std::slice::from_raw_parts(abilities, abilities_length); + let mut abilities_vec = Vec::with_capacity(abilities_length); + for ability in abilities { + abilities_vec.push(CStr::from_ptr(*ability).into()) + } + let hidden_abilities = std::slice::from_raw_parts(hidden_abilities, hidden_abilities_length); + let mut hidden_abilities_vec = Vec::with_capacity(hidden_abilities_length); + for ability in hidden_abilities { + hidden_abilities_vec.push(CStr::from_ptr(*ability).into()) + } + + let flags = std::slice::from_raw_parts(flags, flags_length); + let mut flags_set: HashSet = HashSet::with_capacity(flags_length); + for flag in flags { + flags_set.insert(CStr::from_ptr(*flag).to_str().unwrap().into()); + } + + Box::into_raw(Box::new(Form::new( + &name, + height, + weight, + base_experience, + std::slice::from_raw_parts(types, types_length).to_vec(), + *Box::from_raw(base_stats), + abilities_vec, + hidden_abilities_vec, + *Box::from_raw(moves), + flags_set, + ))) +} + +#[no_mangle] +unsafe extern "C" fn form_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +#[no_mangle] +unsafe extern "C" fn form_name(ptr: ExternPointer) -> OwnedPtr { + let name = ptr.as_ref().name(); + CString::new(name.str()).unwrap().into_raw() +} + +ffi_getter!(Form, height, f32); +ffi_getter!(Form, weight, f32); +ffi_getter!(Form, base_experience, u32); + +ffi_vec_value_getters!(Form, types, TypeIdentifier); + +ffi_getter!(Form, base_stats, BorrowedPtr>); + +ffi_vec_stringkey_getters!(Form, abilities); +ffi_vec_stringkey_getters!(Form, hidden_abilities); + +ffi_getter!(Form, moves, BorrowedPtr); + +#[no_mangle] +unsafe extern "C" fn form_has_flag(ptr: ExternPointer, flag: *const c_char) -> u8 { + let flag = CStr::from_ptr(flag).into(); + if ptr.as_ref().has_flag(&flag) { + 1 + } else { + 0 + } +} diff --git a/src/ffi/static_data/growth_rate.rs b/src/ffi/static_data/growth_rate.rs new file mode 100644 index 0000000..f72d091 --- /dev/null +++ b/src/ffi/static_data/growth_rate.rs @@ -0,0 +1,27 @@ +use crate::defines::LevelInt; +use crate::ffi::{ExternPointer, OwnedPtr}; +use crate::static_data::{GrowthRate, LookupGrowthRate}; +use std::ptr::drop_in_place; + +#[no_mangle] +unsafe extern "C" fn growth_rate_lookup_new(array: *const u32, length: usize) -> OwnedPtr { + let array = std::slice::from_raw_parts(array, length); + Box::into_raw(Box::new(LookupGrowthRate::new(array.to_vec()))) +} + +#[no_mangle] +unsafe extern "C" fn growth_rate_lookup_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +extern "C" fn growth_rate_calculate_level(ptr: ExternPointer, experience: u32) -> LevelInt { + ptr.as_ref().calculate_level(experience) +} + +#[no_mangle] +#[allow(improper_ctypes_definitions)] +extern "C" fn growth_rate_calculate_experience(ptr: ExternPointer, level: LevelInt) -> u32 { + ptr.as_ref().calculate_experience(level) +} diff --git a/src/ffi/static_data/item.rs b/src/ffi/static_data/item.rs new file mode 100644 index 0000000..4a6f342 --- /dev/null +++ b/src/ffi/static_data/item.rs @@ -0,0 +1,49 @@ +use crate::ffi::{ffi_getter, ExternPointer, OwnedPtr}; +use crate::static_data::{BattleItemCategory, Item, ItemCategory}; +use crate::StringKey; +use hashbrown::HashSet; +use std::ffi::{c_char, CStr, CString}; +use std::ptr::drop_in_place; + +#[no_mangle] +unsafe extern "C" fn item_new( + name: *const c_char, + category: ItemCategory, + battle_category: BattleItemCategory, + price: i32, + flags: *const *const c_char, + flags_length: usize, +) -> OwnedPtr { + let flags = std::slice::from_raw_parts(flags, flags_length); + let name: StringKey = CStr::from_ptr(name).to_str().unwrap().into(); + let mut flags_set: HashSet = HashSet::with_capacity(flags_length); + for flag in flags { + flags_set.insert(CStr::from_ptr(*flag).to_str().unwrap().into()); + } + Box::into_raw(Box::new(Item::new(&name, category, battle_category, price, flags_set))) +} + +#[no_mangle] +unsafe extern "C" fn item_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +#[no_mangle] +unsafe extern "C" fn item_name(ptr: ExternPointer) -> OwnedPtr { + let name = ptr.as_ref().name(); + CString::new(name.str()).unwrap().into_raw() +} + +ffi_getter!(Item, category, ItemCategory); +ffi_getter!(Item, battle_category, BattleItemCategory); +ffi_getter!(Item, price, i32); + +#[no_mangle] +unsafe extern "C" fn item_has_flag(ptr: ExternPointer, flag: *const c_char) -> u8 { + let flag = CStr::from_ptr(flag).into(); + if ptr.as_ref().has_flag(&flag) { + 1 + } else { + 0 + } +} diff --git a/src/ffi/static_data/learnable_moves.rs b/src/ffi/static_data/learnable_moves.rs new file mode 100644 index 0000000..7cbd2b5 --- /dev/null +++ b/src/ffi/static_data/learnable_moves.rs @@ -0,0 +1,24 @@ +use crate::defines::LevelInt; +use crate::ffi::{BorrowedPtr, ExternPointer, OwnedPtr}; +use crate::static_data::LearnableMoves; +use std::ffi::{c_char, CStr}; +use std::ptr::drop_in_place; + +#[no_mangle] +extern "C" fn learnable_moves_new() -> OwnedPtr { + Box::into_raw(Box::new(LearnableMoves::new())) +} + +#[no_mangle] +unsafe extern "C" fn learnable_moves_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +#[no_mangle] +unsafe extern "C" fn learnable_moves_add_level_move( + ptr: ExternPointer, + level: LevelInt, + move_name: BorrowedPtr, +) { + ptr.as_mut().add_level_move(level, &CStr::from_ptr(move_name).into()) +} diff --git a/src/ffi/static_data/mod.rs b/src/ffi/static_data/mod.rs new file mode 100644 index 0000000..8e0cd01 --- /dev/null +++ b/src/ffi/static_data/mod.rs @@ -0,0 +1,87 @@ +use crate::ffi::{ExternPointer, OwnedPtr}; +use crate::static_data::EffectParameter; +use std::ffi::{c_char, CStr, CString}; +use std::ptr::drop_in_place; + +mod ability; +mod form; +mod growth_rate; +mod item; +mod learnable_moves; +mod move_data; +mod nature; +mod species; +mod statistic_set; + +#[no_mangle] +extern "C" fn effect_parameter_new_bool(value: u8) -> OwnedPtr { + Box::into_raw(Box::new(EffectParameter::Bool(value == 1))) +} + +#[no_mangle] +extern "C" fn effect_parameter_new_int(value: i64) -> OwnedPtr { + Box::into_raw(Box::new(EffectParameter::Int(value))) +} + +#[no_mangle] +extern "C" fn effect_parameter_new_float(value: f32) -> OwnedPtr { + Box::into_raw(Box::new(EffectParameter::Float(value))) +} + +#[no_mangle] +unsafe extern "C" fn effect_parameter_new_string(value: *const c_char) -> OwnedPtr { + Box::into_raw(Box::new(EffectParameter::String( + CStr::from_ptr(value).to_str().unwrap().into(), + ))) +} + +#[no_mangle] +unsafe extern "C" fn effect_parameter_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +#[no_mangle] +extern "C" fn effect_parameter_get_type(ptr: ExternPointer) -> u8 { + match ptr.as_ref() { + EffectParameter::Bool(_) => 0, + EffectParameter::Int(_) => 1, + EffectParameter::Float(_) => 2, + EffectParameter::String(_) => 3, + } +} + +#[no_mangle] +extern "C" fn effect_parameter_get_as_bool(ptr: ExternPointer) -> u8 { + let p = ptr.as_ref(); + if let EffectParameter::Bool(b) = p { + return if *b { 1 } else { 0 }; + } + panic!("Unexpected effect parameter. Expected bool, was: {}", p); +} + +#[no_mangle] +extern "C" fn effect_parameter_get_as_int(ptr: ExternPointer) -> i64 { + let p = ptr.as_ref(); + if let EffectParameter::Int(b) = p { + return *b; + } + panic!("Unexpected effect parameter. Expected int, was: {}", p); +} + +#[no_mangle] +extern "C" fn effect_parameter_get_as_float(ptr: ExternPointer) -> f32 { + let p = ptr.as_ref(); + if let EffectParameter::Float(b) = p { + return *b; + } + panic!("Unexpected effect parameter. Expected float, was: {}", p); +} + +#[no_mangle] +extern "C" fn effect_parameter_get_as_string(ptr: ExternPointer) -> OwnedPtr { + let p = ptr.as_ref(); + if let EffectParameter::String(b) = p { + return CString::new(b.str().to_string()).unwrap().into_raw(); + } + panic!("Unexpected effect parameter. Expected string, was: {}", p); +} diff --git a/src/ffi/static_data/move_data.rs b/src/ffi/static_data/move_data.rs new file mode 100644 index 0000000..9a6be4d --- /dev/null +++ b/src/ffi/static_data/move_data.rs @@ -0,0 +1,133 @@ +use crate::ffi::{ffi_getter, BorrowedPtr, ExternPointer, OwnedPtr}; +use crate::static_data::{EffectParameter, MoveCategory, MoveData, MoveTarget, SecondaryEffect, TypeIdentifier}; +use crate::StringKey; +use hashbrown::HashSet; +use std::ffi::{c_char, CStr, CString}; +use std::ptr::drop_in_place; + +#[no_mangle] +unsafe extern "C" fn move_data_new( + name: *const c_char, + move_type: TypeIdentifier, + category: MoveCategory, + base_power: u8, + accuracy: u8, + base_usages: u8, + target: MoveTarget, + priority: i8, + secondary_effect: *mut SecondaryEffect, + flags: *const *const c_char, + flags_length: usize, +) -> OwnedPtr { + let flags = std::slice::from_raw_parts(flags, flags_length); + let name: StringKey = CStr::from_ptr(name).to_str().unwrap().into(); + let mut flags_set: HashSet = HashSet::with_capacity(flags_length); + for flag in flags { + flags_set.insert(CStr::from_ptr(*flag).to_str().unwrap().into()); + } + let secondary_effect = if secondary_effect.is_null() { + None + } else { + Some(*Box::from_raw(secondary_effect)) + }; + Box::into_raw(Box::new(MoveData::new( + &name, + move_type, + category, + base_power, + accuracy, + base_usages, + target, + priority, + secondary_effect, + flags_set, + ))) +} + +#[no_mangle] +unsafe extern "C" fn move_data_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +#[no_mangle] +unsafe extern "C" fn move_data_name(ptr: ExternPointer) -> OwnedPtr { + let name = ptr.as_ref().name(); + CString::new(name.str()).unwrap().into_raw() +} + +ffi_getter!(MoveData, move_type, TypeIdentifier); +ffi_getter!(MoveData, category, MoveCategory); +ffi_getter!(MoveData, base_power, u8); +ffi_getter!(MoveData, accuracy, u8); +ffi_getter!(MoveData, base_usages, u8); +ffi_getter!(MoveData, target, MoveTarget); +ffi_getter!(MoveData, priority, i8); + +#[no_mangle] +unsafe extern "C" fn move_data_secondary_effect(ptr: ExternPointer) -> BorrowedPtr { + let effect = ptr.as_ref().secondary_effect(); + if let Some(v) = effect { + v as *const SecondaryEffect + } else { + std::ptr::null() + } +} + +#[no_mangle] +unsafe extern "C" fn move_data_has_flag(ptr: ExternPointer, flag: *const c_char) -> u8 { + let flag = CStr::from_ptr(flag).into(); + if ptr.as_ref().has_flag(&flag) { + 1 + } else { + 0 + } +} + +#[no_mangle] +unsafe extern "C" fn secondary_effect_new( + chance: f32, + effect_name: BorrowedPtr, + parameters: *mut OwnedPtr, + parameters_length: usize, +) -> OwnedPtr { + let parameter_slice = std::slice::from_raw_parts(parameters, parameters_length); + let mut parameters = Vec::with_capacity(parameters_length); + for parameter in parameter_slice { + parameters.push(*Box::from_raw(*parameter)) + } + + Box::into_raw(Box::new(SecondaryEffect::new( + chance, + CStr::from_ptr(effect_name).into(), + parameters, + ))) +} + +#[no_mangle] +unsafe extern "C" fn secondary_effect_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +ffi_getter!(SecondaryEffect, chance, f32); + +#[no_mangle] +unsafe extern "C" fn secondary_effect_effect_name(ptr: ExternPointer) -> OwnedPtr { + CString::new(ptr.as_ref().effect_name().str()).unwrap().into_raw() +} + +#[no_mangle] +unsafe extern "C" fn secondary_effect_parameter_length(ptr: ExternPointer) -> usize { + ptr.as_ref().parameters().len() +} + +#[no_mangle] +unsafe extern "C" fn secondary_effect_parameter_get( + ptr: ExternPointer, + index: usize, +) -> BorrowedPtr { + if let Some(v) = ptr.as_ref().parameters().get(index) { + v as *const EffectParameter + } else { + std::ptr::null() + } +} diff --git a/src/ffi/static_data/nature.rs b/src/ffi/static_data/nature.rs new file mode 100644 index 0000000..1b5cd9a --- /dev/null +++ b/src/ffi/static_data/nature.rs @@ -0,0 +1,31 @@ +use crate::ffi::{ffi_getter, ExternPointer, OwnedPtr}; +use crate::static_data::{Nature, Statistic}; +use std::ptr::drop_in_place; + +#[no_mangle] +extern "C" fn nature_new( + increase_stat: Statistic, + decrease_stat: Statistic, + increase_modifier: f32, + decrease_modifier: f32, +) -> OwnedPtr { + Box::into_raw(Box::new(Nature::new( + increase_stat, + decrease_stat, + increase_modifier, + decrease_modifier, + ))) +} + +#[no_mangle] +unsafe extern "C" fn nature_drop(ptr: OwnedPtr) { + drop_in_place(ptr) +} + +ffi_getter!(Nature, increased_stat, Statistic); +ffi_getter!(Nature, decreased_stat, Statistic); + +#[no_mangle] +extern "C" fn nature_get_stat_modifier(ptr: ExternPointer, stat: Statistic) -> f32 { + ptr.as_ref().get_stat_modifier(stat) +} diff --git a/src/ffi/static_data/species.rs b/src/ffi/static_data/species.rs new file mode 100644 index 0000000..555ad65 --- /dev/null +++ b/src/ffi/static_data/species.rs @@ -0,0 +1,62 @@ +use crate::ffi::{ffi_getter, ffi_stringkey_getter, BorrowedPtr, ExternPointer, OwnedPtr}; +use crate::static_data::{Form, Species}; +use crate::StringKey; +use hashbrown::HashSet; +use std::ffi::{c_char, CStr}; +use std::sync::Arc; + +#[no_mangle] +unsafe extern "C" fn species_new( + id: u16, + name: BorrowedPtr, + gender_rate: f32, + growth_rate: BorrowedPtr, + capture_rate: u8, + default_form: OwnedPtr, + flags: *const *const c_char, + flags_length: usize, +) -> OwnedPtr { + let name: StringKey = CStr::from_ptr(name).to_str().unwrap().into(); + let growth_rate: StringKey = CStr::from_ptr(growth_rate).to_str().unwrap().into(); + + let flags = std::slice::from_raw_parts(flags, flags_length); + let mut flags_set: HashSet = HashSet::with_capacity(flags_length); + for flag in flags { + flags_set.insert(CStr::from_ptr(*flag).to_str().unwrap().into()); + } + Box::into_raw(Box::new(Species::new( + id, + &name, + gender_rate, + &growth_rate, + capture_rate, + *Box::from_raw(default_form), + flags_set, + ))) +} + +ffi_getter!(Species, id, u16); +ffi_stringkey_getter!(Species, name); +ffi_getter!(Species, gender_rate, f32); +ffi_stringkey_getter!(Species, growth_rate); +ffi_getter!(Species, capture_rate, u8); + +#[no_mangle] +unsafe extern "C" fn species_add_form( + species: ExternPointer, + name: BorrowedPtr, + form: OwnedPtr, +) { + let form = *Box::from_raw(form); + species.as_mut().add_form(CStr::from_ptr(name).into(), form) +} + +#[no_mangle] +unsafe extern "C" fn species_get_form(species: ExternPointer, name: BorrowedPtr) -> BorrowedPtr { + let form = species.as_ref().get_form(&CStr::from_ptr(name).into()); + if let Some(form) = form { + Arc::as_ptr(form) + } else { + std::ptr::null() + } +} diff --git a/src/ffi/static_data/statistic_set.rs b/src/ffi/static_data/statistic_set.rs new file mode 100644 index 0000000..90cb867 --- /dev/null +++ b/src/ffi/static_data/statistic_set.rs @@ -0,0 +1,106 @@ +use crate::ffi::{ExternPointer, OwnedPtr}; +use crate::static_data::{StaticStatisticSet, Statistic, StatisticSet}; +use std::ptr::drop_in_place; + +macro_rules! statistic_set { + ($num_type:ident) => { + paste::paste!{ + +#[no_mangle] +extern "C" fn []( + hp: $num_type, + attack: $num_type, + defense: $num_type, + special_attack: $num_type, + special_defense: $num_type, + speed: $num_type, +) -> OwnedPtr> { + Box::into_raw(Box::new(StatisticSet::new( + hp, + attack, + defense, + special_attack, + special_defense, + speed, + ))) +} + +#[no_mangle] +unsafe extern "C" fn [](ptr: OwnedPtr>) { + drop_in_place(ptr) +} + +#[no_mangle] +extern "C" fn [](ptr: ExternPointer>, stat: Statistic) -> $num_type { + ptr.as_ref().get_stat(stat) +} + +#[no_mangle] +extern "C" fn [](ptr: ExternPointer>, stat: Statistic, value: $num_type) { + ptr.as_ref().set_stat(stat, value) +} + +#[no_mangle] +extern "C" fn [](ptr: ExternPointer>, stat: Statistic, value: $num_type) { + ptr.as_ref().increase_stat(stat, value) +} + +#[no_mangle] +extern "C" fn [](ptr: ExternPointer>, stat: Statistic, value: $num_type) { + ptr.as_ref().decrease_stat(stat, value) +} + + } + }; +} + +statistic_set!(u8); +statistic_set!(u16); +statistic_set!(u32); +statistic_set!(i8); +statistic_set!(i16); +statistic_set!(i32); + +macro_rules! static_statistic_set { + ($num_type:ident) => { + paste::paste!{ + +#[no_mangle] +extern "C" fn []( + hp: $num_type, + attack: $num_type, + defense: $num_type, + special_attack: $num_type, + special_defense: $num_type, + speed: $num_type, +) -> OwnedPtr> { + Box::into_raw(Box::new(StaticStatisticSet::new( + hp, + attack, + defense, + special_attack, + special_defense, + speed, + ))) +} + +#[no_mangle] +unsafe extern "C" fn [](ptr: OwnedPtr>) { + drop_in_place(ptr) +} + +#[no_mangle] +extern "C" fn [](ptr: ExternPointer>, stat: Statistic) -> $num_type { + ptr.as_ref().get_stat(stat) +} + + } + }; +} + +static_statistic_set!(u8); +static_statistic_set!(u16); +static_statistic_set!(u32); +static_statistic_set!(i8); +static_statistic_set!(i16); +static_statistic_set!(i32); diff --git a/src/lib.rs b/src/lib.rs index 15b08de..e121ab3 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,9 @@ pub mod defines; /// The dynamic data module holds data that can change during execution, and things that relate to /// this. This includes things as Pokemon themselves, battles, etc. pub mod dynamic_data; +/// The Foreign Function Interface allows for non Rust applications to call this library. +#[cfg(feature = "ffi")] +mod ffi; /// Script implementations handles the different ways that dynamic scripts get loaded during battle. pub mod script_implementations; /// The static data module holds data that can be set once, and then never change. This includes diff --git a/src/static_data/libraries/type_library.rs b/src/static_data/libraries/type_library.rs index 215b69a..c5613f6 100755 --- a/src/static_data/libraries/type_library.rs +++ b/src/static_data/libraries/type_library.rs @@ -6,6 +6,7 @@ use crate::StringKey; /// A unique key that can be used to store a reference to a type. Opaque reference to a byte /// internally. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Atom)] +#[repr(C)] pub struct TypeIdentifier { /// The unique internal value. val: u8, diff --git a/src/static_data/mod.rs b/src/static_data/mod.rs index fc65b3e..aa58926 100755 --- a/src/static_data/mod.rs +++ b/src/static_data/mod.rs @@ -1,3 +1,4 @@ +use crate::StringKey; #[doc(inline)] pub use growth_rates::*; #[doc(inline)] @@ -14,6 +15,7 @@ pub use species_data::*; pub use statistic_set::*; #[doc(inline)] pub use statistics::*; +use std::fmt::{Display, Formatter}; /// Growth rates define how fast a Pokemon can level up. mod growth_rates; @@ -31,3 +33,29 @@ mod species_data; mod statistic_set; /// Statistics are numerical values on Pokemon that are used in battle. mod statistics; + +/// A parameter for an effect. This is basically a simple way to dynamically store multiple different +/// primitives on data. +#[derive(PartialEq, Debug)] +#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] +pub enum EffectParameter { + /// A boolean value. + Bool(bool), + /// An integer value. Stored as a 64 bit int to deal with potentially large numbers. + Int(i64), + /// A float value. Stored as a 32 bit float. + Float(f32), + /// A string value. + String(StringKey), +} + +impl Display for EffectParameter { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + EffectParameter::Bool(v) => f.write_fmt(format_args!("EffectParameter::Bool({})", v)), + EffectParameter::Int(v) => f.write_fmt(format_args!("EffectParameter::Int({})", v)), + EffectParameter::Float(v) => f.write_fmt(format_args!("EffectParameter::Float({})", v)), + EffectParameter::String(v) => f.write_fmt(format_args!("EffectParameter::String({})", v)), + } + } +} diff --git a/src/static_data/moves/move_data.rs b/src/static_data/moves/move_data.rs index 2061935..03feb52 100755 --- a/src/static_data/moves/move_data.rs +++ b/src/static_data/moves/move_data.rs @@ -9,6 +9,7 @@ use crate::StringKey; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[repr(u8)] pub enum MoveCategory { /// A physical move uses the physical attack stats and physical defense stats to calculate damage. Physical = 0, @@ -21,6 +22,7 @@ pub enum MoveCategory { /// The move target defines what kind of targets the move can touch. #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(u8)] pub enum MoveTarget { /// Adjacent allows a move to target any Pokemon that is either directly to the left or right of /// the user, opposed to the user, or left or right of the slot that is opposing the user. diff --git a/src/static_data/moves/secondary_effect.rs b/src/static_data/moves/secondary_effect.rs index e7010b5..72c1f77 100755 --- a/src/static_data/moves/secondary_effect.rs +++ b/src/static_data/moves/secondary_effect.rs @@ -1,20 +1,6 @@ +use crate::static_data::EffectParameter; use crate::StringKey; -/// A parameter for an effect. This is basically a simple way to dynamically store multiple different -/// primitives on data. -#[derive(PartialEq, Debug)] -#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] -pub enum EffectParameter { - /// A boolean value. - Bool(bool), - /// An integer value. Stored as a 64 bit int to deal with potentially large numbers. - Int(i64), - /// A float value. Stored as a 32 bit float. - Float(f32), - /// A string value. - String(StringKey), -} - /// A secondary effect is an effect on a move that happens after it hits. #[derive(PartialEq, Debug)] pub struct SecondaryEffect { diff --git a/src/static_data/natures.rs b/src/static_data/natures.rs index 7054bb9..98a7d13 100755 --- a/src/static_data/natures.rs +++ b/src/static_data/natures.rs @@ -48,9 +48,9 @@ impl Nature { /// Calculates the modifier for a given stat. If it's the increased stat, returns the increased /// modifier, if it's the decreased stat, returns the decreased modifier. Otherwise returns 1.0 pub fn get_stat_modifier(&self, stat: Statistic) -> f32 { - if stat == self.increase_stat { + if stat == self.increase_stat && stat != self.decrease_stat { self.increase_modifier - } else if stat == self.decrease_stat { + } else if stat == self.decrease_stat && stat != self.increase_stat { self.decrease_modifier } else { 1.0 diff --git a/src/utils/string_key.rs b/src/utils/string_key.rs index 4deacdc..d475cfa 100755 --- a/src/utils/string_key.rs +++ b/src/utils/string_key.rs @@ -42,8 +42,7 @@ impl StringKey { /// Creates a new StringKey. If we can find a value for this StringKey in the cache, we re-use /// that value. pub fn new(s: &str) -> Self { - let s: ArcStr = s.into(); - let hash = StringKey::get_hash(s.as_str()); + let hash = Self::get_hash(s); { let cache_read = STRING_CACHE.read(); let cached_value = cache_read.get(&hash); @@ -55,8 +54,15 @@ impl StringKey { } } { - let v = Self { str: s.clone(), hash }; let mut cache_write = STRING_CACHE.write(); + if let Some(cached_value) = cache_write.get(&hash) { + return Self { + str: cached_value.clone(), + hash, + }; + } + let s: ArcStr = s.into(); + let v = Self { str: s.clone(), hash }; cache_write.insert(hash, s); v } @@ -112,7 +118,7 @@ impl Display for StringKey { impl Into for &CStr { fn into(self) -> StringKey { - StringKey::new(self.to_str().unwrap().into()) + StringKey::new(self.to_str().unwrap()) } } /// Converts a character to lowercased in a const safe way.