#![allow(improper_ctypes_definitions)]
use crate::state::{ExportFnEntry, GrugEntityHandle, GrugInitSettings, GrugState, Files, FileInfo};
use crate::ntstring::NTStrPtr;
use crate::types::{GrugFileId, GrugOnFnId, GrugEntity, GrugValue, GameFnPtrState, INVALID_GRUG_SCRIPT_ID};
use crate::error::{Error, GrugError};
use std::ffi::OsString;
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
type CState = (GrugState, UnsafeCell<Option<Error>>, UnsafeCell<Files>, UnsafeCell<Vec<OsString>>);
#[unsafe(no_mangle)]
pub extern "C" fn grug_default_settings() -> GrugInitSettings<'static> {
GrugInitSettings::new()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_init(settings: GrugInitSettings, out_err: &mut MaybeUninit<GrugError<'static>>) -> Option<Box<CState>> {
match settings.build_state() {
Ok(state) => Some(Box::new((state, UnsafeCell::new(None), UnsafeCell::new(Files::empty()), UnsafeCell::new(vec![])))),
Err(err) => {
unsafe{*out_err.as_mut_ptr() = err.leak()};
None
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_deinit(_: Option<Box<CState>>) {}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn grug_register_host_fn<'a>(state: &'a mut CState, game_fn_name: NTStrPtr<'static>, func: GameFnPtrState<GrugState>) -> Option<&'a GrugError<'a>> {
if let Err(err) = unsafe{state.0.register_host_fn(game_fn_name.to_str(), func)} {
Some(state.1.get_mut().insert(err).inner())
} else {
None
}
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_compile_all_files(state: &CState) -> &[FileInfo<'_>] {
let files = unsafe{&mut *state.2.get()};
*files = state.0.compile_all_files();
files.files()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_update(state: &CState) -> &[FileInfo<'_>] {
let files = unsafe{&mut *state.2.get()};
let resources = unsafe{&mut *state.3.get()};
(*resources, *files) = state.0.update_files();
files.files()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_compile_file(state: &CState, file_path: NTStrPtr<'_>) -> GrugFileId {
match state.0.compile_grug_file(file_path.to_str()) {
Ok(id) => id,
Err(err) => {
unsafe{*state.1.get() = Some(err)};
INVALID_GRUG_SCRIPT_ID
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_create_entity(state: &CState, file_id: GrugFileId) -> Option<GrugEntityHandle<'_>> {
state.0.create_entity(file_id)
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_deinit_entity(state: &CState, handle: GrugEntityHandle<'_>) {
state.0.destroy_entity(handle)
}
const INVALID_GRUG_ON_FN_ID: GrugOnFnId = u64::MAX;
#[unsafe(no_mangle)]
pub extern "C" fn grug_get_fn_ids(state: &CState) -> &[ExportFnEntry<'_>] {
state.0.get_export_fns()
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_get_on_fn_id(state: &CState, entity_type: NTStrPtr<'_>, on_fn_name: NTStrPtr<'_>) -> GrugOnFnId {
match state.0.get_export_fn_id(entity_type.to_str(), on_fn_name.to_str()) {
Ok(id) => id,
Err(err) => {
unsafe{*state.1.get() = Some(err)};
INVALID_GRUG_ON_FN_ID
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn grug_call_on_function(state: &CState, entity: &GrugEntity, on_fn_id: GrugOnFnId, values: *const GrugValue, values_len: usize) -> bool {
unsafe{state.0.call_on_function(entity, on_fn_id, std::slice::from_raw_parts(values, values_len))}
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_all_host_fns_registered(state: &CState) -> bool {
let Err(err) = state.0.all_host_fns_registered() else {
return true;
};
unsafe{*state.1.get() = Some(err)};
false
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_get_error<'a>(state: &'a CState) -> Option<&'a GrugError<'a>> {
Some(unsafe{&*state.1.get()}.as_ref()?.inner())
}
#[unsafe(no_mangle)]
pub extern "C" fn grug_entity_get_data<'a>(_: &CState, entity: Option<&'a GrugEntity>) -> Option<&'a GrugEntity> {
entity
}