use std::fmt::{Display, Formatter}; use std::sync::OnceLock; use widestring::{U16CStr, U16CString}; use crate::{Add, Container}; use crate::t_array::TArray; type FStringPrintf = fn(handle: usize, fmt: *const u16); static FSTRING_PRINTF: OnceLock = OnceLock::new(); pub trait Printf { /// Since rust does not support varargs in functions, it is recommended to call format! fn printf>(handle: usize, fmt: A); fn printf_unsafe>(&mut self, fmt: A); } pub struct FString(pub TArray); impl FString { pub fn set_f_string_printf_ptr(f_string_printf: usize) { FSTRING_PRINTF.set(f_string_printf).unwrap(); } } impl Printf for FString { #[inline(always)] fn printf>(handle: usize, fmt: A) { let tmp = U16CString::from_str(fmt).unwrap(); unsafe { std::mem::transmute::(*fstring_printf())( handle, tmp.as_ucstr().as_ptr(), ); } } #[inline(always)] fn printf_unsafe>(&mut self, fmt: A) { let tmp = U16CString::from_str(fmt).unwrap(); let mut tmp_bytes = tmp.into_vec_with_nul(); let new_len = tmp_bytes.len(); if new_len > self.0.cap as usize { // TODO: use Result instead panic!("New string is too long and no printf ffi binding has been provided"); } tmp_bytes.resize(self.0.cap as usize, 0); unsafe { std::ptr::copy_nonoverlapping( tmp_bytes.as_ptr(), self.0.data_ptr as *mut u16, self.0.cap as usize, ); } self.0.len = new_len as u32; self.0.write(); } } impl<'a> Container<&'a U16CStr> for FString { #[inline(always)] fn read_container(&self) -> &'a U16CStr { unsafe { // TODO: Verify this in linux when symphonic is ready U16CStr::from_ptr(self.0.data_ptr as *const u16, self.0.len as usize - 1) }.unwrap() } } impl Add for FString { fn add(&mut self, other: &mut Self) -> &mut Self { let old_len = self.0.resize_grow(Some(other.0.len)); let src = unsafe { std::slice::from_raw_parts_mut(other.0.data_ptr as *mut u16, other.0.len as usize) }; let dst = unsafe { std::slice::from_raw_parts_mut(self.0.data_ptr as *mut u16, self.0.len as usize) }; dst[old_len as usize..other.0.len as usize].copy_from_slice(src); // Clear the header of the original container other.0.release_header_stack(); self } } impl Display for FString { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.read_container().to_string_lossy()) } } #[inline(always)] fn fstring_printf() -> &'static usize { FSTRING_PRINTF.get().unwrap() }