95 lines
No EOL
2.7 KiB
Rust
95 lines
No EOL
2.7 KiB
Rust
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<usize> = OnceLock::new();
|
|
|
|
pub trait Printf<T: ?Sized> {
|
|
/// Since rust does not support varargs in functions, it is recommended to call format!
|
|
fn printf<A: AsRef<T>>(handle: usize, fmt: A);
|
|
fn printf_unsafe<A: AsRef<T>>(&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<str> for FString {
|
|
#[inline(always)]
|
|
fn printf<A: AsRef<str>>(handle: usize, fmt: A) {
|
|
let tmp = U16CString::from_str(fmt).unwrap();
|
|
unsafe {
|
|
std::mem::transmute::<usize, FStringPrintf>(*fstring_printf())(
|
|
handle,
|
|
tmp.as_ucstr().as_ptr(),
|
|
);
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn printf_unsafe<A: AsRef<str>>(&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()
|
|
} |