Add safety for printf
This commit is contained in:
parent
ac8ba74a3e
commit
e07eda21e4
2 changed files with 53 additions and 37 deletions
|
@ -1,12 +1,13 @@
|
|||
[package]
|
||||
name = "unreal-niggery-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
|
||||
[dependencies]
|
||||
widestring = { version = "1.1.0" }
|
||||
thiserror = "2.0.12"
|
||||
widestring = "1.2.0"
|
||||
|
||||
[profile.release]
|
||||
strip = true # Automatically strip symbols from the binary.
|
||||
|
|
|
@ -3,16 +3,29 @@ use std::sync::OnceLock;
|
|||
|
||||
use widestring::{U16CStr, U16CString};
|
||||
|
||||
use crate::{Add, Container};
|
||||
use crate::t_array::TArray;
|
||||
use crate::{Add, Container};
|
||||
|
||||
type FStringPrintf = fn(handle: usize, fmt: *const u16);
|
||||
static FSTRING_PRINTF: OnceLock<usize> = OnceLock::new();
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("ContainsNul<u16> error: {0}")]
|
||||
ContainsNulu16(#[from] widestring::error::ContainsNul<u16>),
|
||||
#[error("New string is too long and no printf ffi binding has been provided")]
|
||||
DataTooLongForUnsafe,
|
||||
#[error("FStringPrintf not set")]
|
||||
FStringPrintfNotSet,
|
||||
}
|
||||
|
||||
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);
|
||||
fn printf<A: AsRef<T>>(handle: usize, fmt: A) -> Result<Self, Error> where Self: Sized;
|
||||
/// Since rust does not support varargs in functions, it is recommended to call format!
|
||||
fn printf_safe<A: AsRef<T>>(&mut self, fmt: A) -> Result<Self, Error> where Self: Sized;
|
||||
/// Since rust does not support varargs in functions, it is recommended to call format!
|
||||
fn printf_unsafe<A: AsRef<T>>(&mut self, fmt: A) -> Result<Self, Error> where Self: Sized;
|
||||
}
|
||||
|
||||
pub struct FString(pub TArray);
|
||||
|
@ -25,24 +38,29 @@ impl FString {
|
|||
|
||||
impl Printf<str> for FString {
|
||||
#[inline(always)]
|
||||
fn printf<A: AsRef<str>>(handle: usize, fmt: A) {
|
||||
let tmp = U16CString::from_str(fmt).unwrap();
|
||||
fn printf<A: AsRef<str>>(handle: usize, fmt: A) -> Result<Self, Error> {
|
||||
let tmp = U16CString::from_str(fmt)?;
|
||||
unsafe {
|
||||
std::mem::transmute::<usize, FStringPrintf>(*fstring_printf())(
|
||||
handle,
|
||||
tmp.as_ucstr().as_ptr(),
|
||||
);
|
||||
std::mem::transmute::<usize, FStringPrintf>(
|
||||
*FSTRING_PRINTF.get().ok_or(Error::FStringPrintfNotSet)?,
|
||||
)(handle, tmp.as_ucstr().as_ptr());
|
||||
}
|
||||
Ok(Self(TArray::read(handle)))
|
||||
}
|
||||
|
||||
fn printf_safe<A: AsRef<str>>(&mut self, fmt: A) -> Result<Self, Error> {
|
||||
match FSTRING_PRINTF.get() {
|
||||
None => self.printf_unsafe(fmt),
|
||||
Some(_) => Self::printf(self.0.ptr as usize, fmt),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn printf_unsafe<A: AsRef<str>>(&mut self, fmt: A) {
|
||||
let tmp = U16CString::from_str(fmt).unwrap();
|
||||
fn printf_unsafe<A: AsRef<str>>(&mut self, fmt: A) -> Result<Self, Error> {
|
||||
let tmp = U16CString::from_str(fmt)?;
|
||||
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");
|
||||
return Err(Error::DataTooLongForUnsafe);
|
||||
}
|
||||
tmp_bytes.resize(self.0.cap as usize, 0);
|
||||
unsafe {
|
||||
|
@ -54,6 +72,7 @@ impl Printf<str> for FString {
|
|||
}
|
||||
self.0.len = new_len as u32;
|
||||
self.0.write();
|
||||
Ok(FString(TArray::read(self.0.ptr as usize)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +82,8 @@ impl<'a> Container<&'a U16CStr> for FString {
|
|||
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()
|
||||
}
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,8 +108,3 @@ impl Display for FString {
|
|||
write!(f, "{}", self.read_container().to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn fstring_printf() -> &'static usize {
|
||||
FSTRING_PRINTF.get().unwrap()
|
||||
}
|
Loading…
Reference in a new issue