1
0

feat: update bmap-rs

This commit is contained in:
2026-02-18 20:23:54 +08:00
parent 6ea43cd82f
commit cf0966e6d3
6 changed files with 189 additions and 29 deletions

View File

@@ -1,14 +1,17 @@
//! The module includes all senior wrappers for BMap FFI calling in Rust style.
//!
//! This module is what user of this library should use.
use crate::bmap::{self, BMBOOL, CKDWORD, CKID, CKSTRING, PBMVOID};
use crate::marshaler;
mod marshaler;
mod utilities;
use crate::bmap::{self, BMBOOL, CKDWORD, CKID, CKINT, CKSTRING, PBMVOID};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::iter::{ExactSizeIterator, FusedIterator, Iterator};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::sync::LazyLock;
use std::sync::{Mutex, OnceLock};
use thiserror::Error as TeError;
// region: Error and Result Types
@@ -22,6 +25,8 @@ pub enum Error {
Marshaler(#[from] marshaler::Error),
#[error("the length of given data iterator is too short when assigning struct array.")]
OutOfLength,
#[error("bad cast to integral value")]
BadIntCast(#[from] std::num::TryFromIntError),
}
/// The result type used in this module.
@@ -227,16 +232,70 @@ macro_rules! arg_out {
///
/// This struct make sure that all BMap calling is executed under initialized BMap environment,
/// and shutdown BMap environment automatically when there is no more calling.
struct BMapGuard {}
pub struct BMap {}
impl BMapGuard {
impl BMap {
pub fn instance() -> &'static Result<Mutex<BMap>> {
static INSTANCE: OnceLock<Result<Mutex<BMap>>> = OnceLock::new();
INSTANCE.get_or_init(|| BMap::new().map(|v| Mutex::new(v)))
}
pub fn is_available() -> bool {
Self::instance().is_ok()
}
pub fn with_bmap<F, R>(f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
let mut instance = Self::instance()
.as_ref()
.expect("bad BMap environment (not initialized)")
.lock()
.expect("failed mutex lock");
f(&mut instance)
}
}
impl BMap {
pub fn create_file_reader<'a, T>(
&'a mut self,
file_name: &str,
temp_folder: &str,
texture_folder: &str,
encodings: &[T],
) -> Result<BMFileReader<'a>>
where
T: Into<Vec<u8>> + Copy,
{
BMFileReader::new(self, file_name, temp_folder, texture_folder, encodings)
}
pub fn create_file_writer<'a, T>(
&'a mut self,
temp_folder: &str,
texture_folder: &str,
encodings: &[T],
) -> Result<BMFileWriter<'a>>
where
T: Into<Vec<u8>> + Copy,
{
BMFileWriter::new(self, temp_folder, texture_folder, encodings)
}
pub fn create_mesh_trans<'a>(&'a mut self) -> Result<BMMeshTrans<'a>> {
BMMeshTrans::new(self)
}
}
impl BMap {
fn new() -> Result<Self> {
bmap_exec!(bmap::BMInit());
Ok(Self {})
}
}
impl Drop for BMapGuard {
impl Drop for BMap {
fn drop(&mut self) {
// Ignore return of this function by design.
// Because using Result in Drop is inviable,
@@ -245,16 +304,6 @@ impl Drop for BMapGuard {
}
}
static BMAP_SINGLETON: LazyLock<Result<BMapGuard>> = LazyLock::new(|| BMapGuard::new());
fn get_bmap_guard_singleton() -> Result<&'static BMapGuard> {
BMAP_SINGLETON.as_ref().map_err(|e| e.clone())
}
pub fn is_bmap_available() -> bool {
BMAP_SINGLETON.is_ok()
}
// endregion
// region: BMFileReader
@@ -277,11 +326,12 @@ pub fn is_bmap_available() -> bool {
pub struct BMFileReader<'a> {
handle: PBMVOID,
phantom: PhantomData<&'a BMapGuard>,
phantom: PhantomData<&'a BMap>,
}
impl<'a> BMFileReader<'a> {
pub fn new<T>(
fn new<T>(
_: &'a BMap,
file_name: &str,
temp_folder: &str,
texture_folder: &str,
@@ -292,11 +342,29 @@ impl<'a> BMFileReader<'a> {
{
let file_name = unsafe { marshaler::to_native_string(file_name)? };
let temp_folder = unsafe { marshaler::to_native_string(temp_folder)? };
let texture_folder = unsafe { marshaler::to_native_string(texture_folder) }?;
let texture_folder = unsafe { marshaler::to_native_string(texture_folder)? };
let encodings = unsafe { marshaler::to_native_string_array(encodings)? };
let mut file = MaybeUninit::<PBMVOID>::uninit();
bmap_exec!(bmap::BMFile_Load(
file_name.as_raw(),
temp_folder.as_raw(),
texture_folder.as_raw(),
bmap_rs_callback,
encodings.len().try_into()?,
encodings.as_raw(),
arg_out!(file.as_mut_ptr(), PBMVOID)
));
Ok(Self {
handle: unsafe { file.assume_init() },
phantom: PhantomData::<&'a BMap>,
})
}
}
impl<'a> BMFileReader<'a> {}
// impl<'a> BMFileReader<'a> {
// fn get_generic_object_count<O, FC>(&self, fc: FnProtoGetCount) -> Result<usize> {
@@ -309,6 +377,12 @@ impl<'a> BMFileReader<'a> {
// }
impl<'a> Drop for BMFileReader<'a> {
fn drop(&mut self) {
let _ = unsafe { bmap::BMFile_Free(self.handle) };
}
}
impl<'a> AbstractPointer for BMFileReader<'a> {
unsafe fn get_pointer(&self) -> PBMVOID {
self.handle
@@ -323,7 +397,64 @@ impl<'a> BMFileRW for BMFileReader<'a> {}
pub struct BMFileWriter<'a> {
handle: PBMVOID,
phantom: PhantomData<&'a BMapGuard>,
phantom: PhantomData<&'a BMap>,
}
impl<'a> BMFileWriter<'a> {
fn new<T>(_: &'a BMap, temp_folder: &str, texture_folder: &str, encodings: &[T]) -> Result<Self>
where
T: Into<Vec<u8>> + Copy,
{
let temp_folder = unsafe { marshaler::to_native_string(temp_folder)? };
let texture_folder = unsafe { marshaler::to_native_string(texture_folder)? };
let encodings = unsafe { marshaler::to_native_string_array(encodings)? };
let mut file = MaybeUninit::<PBMVOID>::uninit();
bmap_exec!(bmap::BMFile_Create(
temp_folder.as_raw(),
texture_folder.as_raw(),
bmap_rs_callback,
encodings.len().try_into()?,
encodings.as_raw(),
arg_out!(file.as_mut_ptr(), PBMVOID)
));
Ok(Self {
handle: unsafe { file.assume_init() },
phantom: PhantomData::<&'a BMap>,
})
}
}
impl<'a> BMFileWriter<'a> {
pub fn save(
&self,
filename: &str,
texture_save_opt: bmap::CK_TEXTURE_SAVEOPTIONS,
compress_level: Option<CKINT>,
) -> Result<()> {
let filename = unsafe { marshaler::to_native_string(filename)? };
let use_compress = compress_level.is_some();
let compress_level = compress_level.unwrap_or(5);
bmap_exec!(bmap::BMFile_Save(
self.get_pointer(),
filename.as_raw(),
texture_save_opt,
use_compress,
compress_level
));
Ok(())
}
}
impl<'a> BMFileWriter<'a> {}
impl<'a> Drop for BMFileWriter<'a> {
fn drop(&mut self) {
let _ = unsafe { bmap::BMFile_Free(self.handle) };
}
}
impl<'a> AbstractPointer for BMFileWriter<'a> {
@@ -483,12 +614,12 @@ pub trait BMMaterial: BMObject {
set_copyable_value(self, bmap::BMMaterial_SetSpecularPower, power)
}
fn get_texture(&self) -> Result<Option<Box<dyn BMTexture>>> {
todo!();
}
fn set_texture(&mut self, texture: Option<&dyn BMTexture>) -> Result<()> {
todo!();
}
// fn get_texture(&self) -> Result<Option<Box<dyn BMTexture>>> {
// todo!();
// }
// fn set_texture(&mut self, texture: Option<&dyn BMTexture>) -> Result<()> {
// todo!();
// }
fn get_texture_border_color(&self) -> Result<bmap::VxColor> {
let intermediary = get_copyable_value(self, bmap::BMMaterial_GetTextureBorderColor)?;
@@ -950,7 +1081,24 @@ libobj_impl_obj_trait!(BMGroupImpl, BMGroup);
pub struct BMMeshTrans<'a> {
handle: PBMVOID,
phantom: PhantomData<&'a BMapGuard>,
phantom: PhantomData<&'a BMap>,
}
impl<'a> BMMeshTrans<'a> {
fn new(_: &'a BMap) -> Result<Self> {
let mut trans = MaybeUninit::<PBMVOID>::uninit();
bmap_exec!(bmap::BMMeshTrans_New(arg_out!(trans.as_mut_ptr(), PBMVOID)));
Ok(Self {
handle: unsafe { trans.assume_init() },
phantom: PhantomData::<&'a BMap>,
})
}
}
impl<'a> Drop for BMMeshTrans<'a> {
fn drop(&mut self) {
let _ = unsafe { bmap::BMMeshTrans_Delete(self.handle) };
}
}
impl<'a> AbstractPointer for BMMeshTrans<'a> {

View File

@@ -0,0 +1,4 @@
//! BMap wrapper used utility stuff

View File

@@ -1,5 +1,4 @@
//! The Rust binding to BMap.
pub mod virtools_types;
pub mod bmap;
pub(crate) mod marshaler;
pub mod bmap_wrapper;

View File

@@ -0,0 +1,9 @@
use bmap_rs::bmap_wrapper as bmap;
#[test]
fn test_complete() {
assert!(bmap::BMap::is_available());
bmap::BMap::with_bmap(|b| {
let r = b.create_file_reader("", "", "", &[""]);
});
}

View File

@@ -1,7 +1,7 @@
use bmap_rs::bmap;
#[test]
fn test_init_and_dispose() {
fn test_raw() {
assert!(unsafe { bmap::BMInit() });
assert!(unsafe { bmap::BMDispose() });
}