Initial commit
This commit is contained in:
commit
9b582c49bf
7 changed files with 265 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
16
Cargo.lock
generated
Normal file
16
Cargo.lock
generated
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unreal-niggery-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"widestring",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "unreal-niggery-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
widestring = { version = "1.1.0" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true # Automatically strip symbols from the binary.
|
||||||
|
lto = true # Link-time optimization.
|
||||||
|
opt-level = 3 # Optimization level 3.
|
||||||
|
codegen-units = 1 # Maximum size reduction optimizations.
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# unreal-niggery-rs
|
||||||
|
|
||||||
|
Subset of UE code ported to rust to be used from Rust code. Some functions might require the use of UE Garbage collector so some FFI calls might be required
|
||||||
|
|
||||||
|
|
72
src/f_string.rs
Normal file
72
src/f_string.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use widestring::{WideCStr, WideCString};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FString(pub(crate) 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 = WideCString::from_str(fmt).unwrap();
|
||||||
|
unsafe {
|
||||||
|
std::mem::transmute::<usize, FStringPrintf>(*fstring_printf())(
|
||||||
|
handle,
|
||||||
|
tmp.as_ucstr().as_ptr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Container<&'a WideCStr> for FString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_container(&self) -> &'a WideCStr {
|
||||||
|
unsafe {
|
||||||
|
WideCStr::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()
|
||||||
|
}
|
10
src/lib.rs
Normal file
10
src/lib.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
pub trait Container<T> {
|
||||||
|
fn read_container(&self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Add<T = Self> {
|
||||||
|
fn add(&mut self, other: &mut T) -> &mut T;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod t_array;
|
||||||
|
pub mod f_string;
|
146
src/t_array.rs
Normal file
146
src/t_array.rs
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use widestring::WideCStr;
|
||||||
|
|
||||||
|
use crate::{Add, Container};
|
||||||
|
use crate::f_string::FString;
|
||||||
|
|
||||||
|
const T_ARRAY_HEADER_SIZE: usize = 0x10;
|
||||||
|
const DATA_PTR_OFFSET: usize = 0x00;
|
||||||
|
const LEN_OFFSET: usize = 0x08;
|
||||||
|
const CAP_OFFSET: usize = 0x0C;
|
||||||
|
|
||||||
|
type TArrayResize = fn(ptr: usize, len: u32);
|
||||||
|
static T_ARRAY_RESIZE_GROW: OnceLock<usize> = OnceLock::new();
|
||||||
|
|
||||||
|
pub struct TArray {
|
||||||
|
pub(crate) ptr: *const u8,
|
||||||
|
pub(crate) data_ptr: u64,
|
||||||
|
pub(crate) len: u32,
|
||||||
|
pub(crate) cap: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TArray {
|
||||||
|
pub fn set_t_array_resize_grow_ptr(t_array_resize_grow: usize) {
|
||||||
|
T_ARRAY_RESIZE_GROW.set(t_array_resize_grow).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(ptr: usize) -> Self {
|
||||||
|
let data = unsafe { std::slice::from_raw_parts(ptr as *const u8, T_ARRAY_HEADER_SIZE) };
|
||||||
|
Self {
|
||||||
|
ptr: ptr as *const u8,
|
||||||
|
data_ptr: u64::from_le_bytes(data[DATA_PTR_OFFSET..LEN_OFFSET].try_into().unwrap()),
|
||||||
|
len: u32::from_le_bytes(data[LEN_OFFSET..CAP_OFFSET].try_into().unwrap()),
|
||||||
|
cap: u32::from_le_bytes(data[CAP_OFFSET..T_ARRAY_HEADER_SIZE].try_into().unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn resize_grow(&mut self, count: Option<u32>) -> u32 {
|
||||||
|
let count = count.unwrap_or(1);
|
||||||
|
let old_len = self.len;
|
||||||
|
self.len = old_len + count;
|
||||||
|
let data = unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(self.ptr as *mut u8, T_ARRAY_HEADER_SIZE)
|
||||||
|
};
|
||||||
|
self.write_len_internal(data);
|
||||||
|
if self.cap < old_len + count {
|
||||||
|
unsafe {
|
||||||
|
std::mem::transmute::<usize, TArrayResize>(*t_array_resize_grow())(
|
||||||
|
self.ptr as usize,
|
||||||
|
old_len,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// ResizeGrow will change capacity and might change data_ptr, so force refresh
|
||||||
|
self.data_ptr = u64::from_le_bytes(data[DATA_PTR_OFFSET..LEN_OFFSET].try_into().unwrap());
|
||||||
|
self.len = u32::from_le_bytes(data[LEN_OFFSET..CAP_OFFSET].try_into().unwrap());
|
||||||
|
self.cap = u32::from_le_bytes(data[CAP_OFFSET..T_ARRAY_HEADER_SIZE].try_into().unwrap());
|
||||||
|
}
|
||||||
|
old_len
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn release_header_stack(&mut self) {
|
||||||
|
let data = unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(self.ptr as *mut u8, T_ARRAY_HEADER_SIZE)
|
||||||
|
};
|
||||||
|
data.fill(0);
|
||||||
|
self.data_ptr = u64::from_le_bytes(data[DATA_PTR_OFFSET..LEN_OFFSET].try_into().unwrap());
|
||||||
|
self.len = u32::from_le_bytes(data[LEN_OFFSET..CAP_OFFSET].try_into().unwrap());
|
||||||
|
self.cap = u32::from_le_bytes(data[CAP_OFFSET..T_ARRAY_HEADER_SIZE].try_into().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write(&self) {
|
||||||
|
let data = unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(self.ptr as *mut u8, T_ARRAY_HEADER_SIZE)
|
||||||
|
};
|
||||||
|
self.write_data_ptr_internal(data);
|
||||||
|
self.write_len_internal(data);
|
||||||
|
self.write_cap_internal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_data_ptr_internal(&self, data: &mut [u8]) {
|
||||||
|
let bytes = self.data_ptr.to_le_bytes();
|
||||||
|
data[DATA_PTR_OFFSET..LEN_OFFSET].copy_from_slice(&bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_len_internal(&self, data: &mut [u8]) {
|
||||||
|
let bytes = self.len.to_le_bytes();
|
||||||
|
data[LEN_OFFSET..CAP_OFFSET].copy_from_slice(&bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_cap_internal(&self, data: &mut [u8]) {
|
||||||
|
let bytes = self.cap.to_le_bytes();
|
||||||
|
data[CAP_OFFSET..T_ARRAY_HEADER_SIZE].copy_from_slice(&bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Container<Vec<&'a WideCStr>> for TArray {
|
||||||
|
fn read_container(&self) -> Vec<&'a WideCStr> {
|
||||||
|
let mut result = Vec::with_capacity(self.len as usize);
|
||||||
|
for i in 0..self.len {
|
||||||
|
result.push(
|
||||||
|
FString(
|
||||||
|
Self::read(self.data_ptr as usize + (i as usize * T_ARRAY_HEADER_SIZE))
|
||||||
|
).read_container()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for TArray {
|
||||||
|
fn add(&mut self, other: &mut Self) -> &mut Self {
|
||||||
|
let old_len = self.resize_grow(None);
|
||||||
|
Self {
|
||||||
|
ptr: (self.data_ptr as usize + (old_len as usize * T_ARRAY_HEADER_SIZE)) as *const u8,
|
||||||
|
data_ptr: other.data_ptr,
|
||||||
|
len: other.len,
|
||||||
|
cap: other.cap,
|
||||||
|
}.write();
|
||||||
|
// Clear the of the original container
|
||||||
|
other.release_header_stack();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TArray {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let elements = self.read_container();
|
||||||
|
writeln!(f, "[")?;
|
||||||
|
for element in elements {
|
||||||
|
writeln!(f, "\t- {}", element.to_string_lossy())?;
|
||||||
|
}
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn t_array_resize_grow() -> &'static usize {
|
||||||
|
T_ARRAY_RESIZE_GROW.get().unwrap()
|
||||||
|
}
|
Loading…
Reference in a new issue