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