//! Cubism Core FFI 绑定 //! //! 直接对应 `Live2DCubismCore.h` 的 C API。 //! 动态加载 libLive2DCubismCore.so(需 libm.so 预加载,因 Core 内部使用 powf)。 use libloading::{Library, Symbol}; use std::ffi::CString; use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void}; /// moc 对齐要求(字节) pub const CSM_ALIGNOF_MOC: usize = 64; /// model 对齐要求(字节) pub const CSM_ALIGNOF_MODEL: usize = 16; /// 可绘制常量标志位 pub mod constant_flags { pub const BLEND_ADDITIVE: u8 = 1 << 0; pub const BLEND_MULTIPLICATIVE: u8 = 1 << 1; pub const IS_DOUBLE_SIDED: u8 = 1 << 2; pub const IS_INVERTED_MASK: u8 = 1 << 3; } /// 可绘制动态标志位 pub mod dynamic_flags { pub const IS_VISIBLE: u8 = 1 << 0; pub const VISIBILITY_DID_CHANGE: u8 = 1 << 1; pub const OPACITY_DID_CHANGE: u8 = 1 << 2; pub const DRAW_ORDER_DID_CHANGE: u8 = 1 << 3; pub const RENDER_ORDER_DID_CHANGE: u8 = 1 << 4; pub const VERTEX_POSITIONS_DID_CHANGE: u8 = 1 << 5; pub const BLEND_COLOR_DID_CHANGE: u8 = 1 << 6; } /// 混合模式 pub mod blend_mode { pub const NORMAL: c_int = 0; pub const ADDITIVE: c_int = 1; pub const MULTIPLICATIVE: c_int = 2; } #[repr(C)] #[derive(Clone, Copy, Debug, Default)] pub struct CsmVector2 { pub x: f32, pub y: f32, } #[repr(C)] #[derive(Clone, Copy, Debug, Default)] pub struct CsmVector4 { pub x: f32, pub y: f32, pub z: f32, pub w: f32, } /// Cubism Core 动态加载封装 pub struct CubismCore { _libm: Library, _lib: Library, pub csm_get_version: unsafe extern "C" fn() -> c_uint, pub csm_get_latest_moc_version: unsafe extern "C" fn() -> c_uint, pub csm_get_moc_version: unsafe extern "C" fn(address: *const c_void, size: c_uint) -> c_uint, pub csm_has_moc_consistency: unsafe extern "C" fn(address: *mut c_void, size: c_uint) -> c_int, pub csm_revive_moc_in_place: unsafe extern "C" fn(address: *mut c_void, size: c_uint) -> *mut c_void, pub csm_get_sizeof_model: unsafe extern "C" fn(moc: *const c_void) -> c_uint, pub csm_initialize_model_in_place: unsafe extern "C" fn( moc: *const c_void, address: *mut c_void, size: c_uint, ) -> *mut c_void, pub csm_update_model: unsafe extern "C" fn(model: *mut c_void), pub csm_get_render_orders: unsafe extern "C" fn(model: *const c_void) -> *const c_int, pub csm_read_canvas_info: unsafe extern "C" fn( model: *const c_void, out_size: *mut CsmVector2, out_origin: *mut CsmVector2, out_ppu: *mut f32, ), pub csm_get_parameter_count: unsafe extern "C" fn(model: *const c_void) -> c_int, pub csm_get_parameter_ids: unsafe extern "C" fn(model: *const c_void) -> *const *const c_char, pub csm_get_parameter_minimum_values: unsafe extern "C" fn(model: *const c_void) -> *const f32, pub csm_get_parameter_maximum_values: unsafe extern "C" fn(model: *const c_void) -> *const f32, pub csm_get_parameter_default_values: unsafe extern "C" fn(model: *const c_void) -> *const f32, pub csm_get_parameter_values: unsafe extern "C" fn(model: *mut c_void) -> *mut f32, pub csm_get_part_count: unsafe extern "C" fn(model: *const c_void) -> c_int, pub csm_get_part_opacities: unsafe extern "C" fn(model: *mut c_void) -> *mut f32, pub csm_get_drawable_count: unsafe extern "C" fn(model: *const c_void) -> c_int, pub csm_get_drawable_ids: unsafe extern "C" fn(model: *const c_void) -> *const *const c_char, pub csm_get_drawable_constant_flags: unsafe extern "C" fn(model: *const c_void) -> *const c_uchar, pub csm_get_drawable_dynamic_flags: unsafe extern "C" fn(model: *const c_void) -> *const c_uchar, pub csm_get_drawable_blend_modes: unsafe extern "C" fn(model: *const c_void) -> *const c_int, pub csm_get_drawable_texture_indices: unsafe extern "C" fn(model: *const c_void) -> *const c_int, pub csm_get_drawable_draw_orders: unsafe extern "C" fn(model: *const c_void) -> *const c_int, pub csm_get_drawable_opacities: unsafe extern "C" fn(model: *const c_void) -> *const f32, pub csm_get_drawable_mask_counts: unsafe extern "C" fn(model: *const c_void) -> *const c_int, pub csm_get_drawable_masks: unsafe extern "C" fn(model: *const c_void) -> *const *const c_int, pub csm_get_drawable_vertex_counts: unsafe extern "C" fn(model: *const c_void) -> *const c_int, pub csm_get_drawable_vertex_positions: unsafe extern "C" fn(model: *const c_void) -> *const *const CsmVector2, pub csm_get_drawable_vertex_uvs: unsafe extern "C" fn(model: *const c_void) -> *const *const CsmVector2, pub csm_get_drawable_index_counts: unsafe extern "C" fn(model: *const c_void) -> *const c_int, pub csm_get_drawable_indices: unsafe extern "C" fn(model: *const c_void) -> *const *const u16, pub csm_get_drawable_multiply_colors: unsafe extern "C" fn(model: *const c_void) -> *const CsmVector4, pub csm_get_drawable_screen_colors: unsafe extern "C" fn(model: *const c_void) -> *const CsmVector4, pub csm_reset_drawable_dynamic_flags: unsafe extern "C" fn(model: *mut c_void), } impl CubismCore { /// 加载 Cubism Core 动态库 /// /// `so_path` 为 libLive2DCubismCore.so 的路径。 /// 内部会先 dlopen libm.so(Core 依赖 powf 但 NEEDED 未声明)。 pub fn load(so_path: &str) -> anyhow::Result { // 预加载 libm,RTLD_GLOBAL 让后续 .so 能解析到 powf let libm = unsafe { Library::new("libm.so.6").or_else(|_| Library::new("libm.so.6.1"))? }; let lib = unsafe { Library::new(so_path)? }; unsafe { Ok(Self { csm_get_version: *lib.get(b"csmGetVersion\0")?, csm_get_latest_moc_version: *lib.get(b"csmGetLatestMocVersion\0")?, csm_get_moc_version: *lib.get(b"csmGetMocVersion\0")?, csm_has_moc_consistency: *lib.get(b"csmHasMocConsistency\0")?, csm_revive_moc_in_place: *lib.get(b"csmReviveMocInPlace\0")?, csm_get_sizeof_model: *lib.get(b"csmGetSizeofModel\0")?, csm_initialize_model_in_place: *lib.get(b"csmInitializeModelInPlace\0")?, csm_update_model: *lib.get(b"csmUpdateModel\0")?, csm_get_render_orders: *lib.get(b"csmGetRenderOrders\0")?, csm_read_canvas_info: *lib.get(b"csmReadCanvasInfo\0")?, csm_get_parameter_count: *lib.get(b"csmGetParameterCount\0")?, csm_get_parameter_ids: *lib.get(b"csmGetParameterIds\0")?, csm_get_parameter_minimum_values: *lib .get(b"csmGetParameterMinimumValues\0")?, csm_get_parameter_maximum_values: *lib .get(b"csmGetParameterMaximumValues\0")?, csm_get_parameter_default_values: *lib .get(b"csmGetParameterDefaultValues\0")?, csm_get_parameter_values: *lib.get(b"csmGetParameterValues\0")?, csm_get_part_count: *lib.get(b"csmGetPartCount\0")?, csm_get_part_opacities: *lib.get(b"csmGetPartOpacities\0")?, csm_get_drawable_count: *lib.get(b"csmGetDrawableCount\0")?, csm_get_drawable_ids: *lib.get(b"csmGetDrawableIds\0")?, csm_get_drawable_constant_flags: *lib .get(b"csmGetDrawableConstantFlags\0")?, csm_get_drawable_dynamic_flags: *lib .get(b"csmGetDrawableDynamicFlags\0")?, csm_get_drawable_blend_modes: *lib.get(b"csmGetDrawableBlendModes\0")?, csm_get_drawable_texture_indices: *lib .get(b"csmGetDrawableTextureIndices\0")?, csm_get_drawable_draw_orders: *lib.get(b"csmGetDrawableDrawOrders\0")?, csm_get_drawable_opacities: *lib.get(b"csmGetDrawableOpacities\0")?, csm_get_drawable_mask_counts: *lib.get(b"csmGetDrawableMaskCounts\0")?, csm_get_drawable_masks: *lib.get(b"csmGetDrawableMasks\0")?, csm_get_drawable_vertex_counts: *lib .get(b"csmGetDrawableVertexCounts\0")?, csm_get_drawable_vertex_positions: *lib .get(b"csmGetDrawableVertexPositions\0")?, csm_get_drawable_vertex_uvs: *lib.get(b"csmGetDrawableVertexUvs\0")?, csm_get_drawable_index_counts: *lib.get(b"csmGetDrawableIndexCounts\0")?, csm_get_drawable_indices: *lib.get(b"csmGetDrawableIndices\0")?, csm_get_drawable_multiply_colors: *lib .get(b"csmGetDrawableMultiplyColors\0")?, csm_get_drawable_screen_colors: *lib .get(b"csmGetDrawableScreenColors\0")?, csm_reset_drawable_dynamic_flags: *lib .get(b"csmResetDrawableDynamicFlags\0")?, _libm: libm, _lib: lib, }) } } /// 查询 Core 版本(返回原始 u32,格式 0xMMmmpppp) pub fn version(&self) -> u32 { unsafe { (self.csm_get_version)() } } /// 格式化版本号为 "major.minor.patch" pub fn version_string(&self) -> String { let v = self.version(); format!( "{}.{}.{}", (v >> 24) & 0xFF, (v >> 16) & 0xFF, v & 0xFFFF ) } } /// 分配对齐内存(返回对齐指针和底层分配,底层分配需保持存活) pub fn alloc_aligned(size: usize, align: usize) -> anyhow::Result { let layout = std::alloc::Layout::from_size_align(size, align)?; let ptr = unsafe { std::alloc::alloc(layout) }; if ptr.is_null() { anyhow::bail!("aligned alloc failed for size={size} align={align}"); } Ok(AlignedBuffer { ptr, layout }) } pub struct AlignedBuffer { ptr: *mut u8, layout: std::alloc::Layout, } impl AlignedBuffer { pub fn as_ptr(&self) -> *mut u8 { self.ptr } pub fn as_mut_void(&mut self) -> *mut c_void { self.ptr as *mut c_void } pub fn as_void(&self) -> *const c_void { self.ptr as *const c_void } } impl Drop for AlignedBuffer { fn drop(&mut self) { unsafe { std::alloc::dealloc(self.ptr, self.layout) } } } unsafe impl Send for AlignedBuffer {} unsafe impl Sync for AlignedBuffer {} /// 将 C 字符串指针转换为 Rust String(空指针返回 None) pub unsafe fn cstr_ptr_to_string(ptr: *const c_char) -> Option { if ptr.is_null() { return None; } let raw = std::slice::from_raw_parts(ptr as *const u8, libc_strlen(ptr)); Some(String::from_utf8_lossy(raw).into_owned()) } /// 简易 strlen(避免依赖 libc crate) unsafe fn libc_strlen(ptr: *const c_char) -> usize { let mut p = ptr as *const u8; let mut n = 0; while *p != 0 { p = p.add(1); n += 1; } n } // 保留 CString 防止 unused 警告(未来日志函数需要) #[allow(dead_code)] fn _ensure_cstring_linked() -> CString { CString::new("").unwrap() }