unreal-niggery-rs/src/f_string.rs
2025-04-20 20:24:19 +07:00

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()
}