Skip to content

Commit

Permalink
Implement constructing FName
Browse files Browse the repository at this point in the history
  • Loading branch information
trumank committed Jun 7, 2024
1 parent 113f7d5 commit 5896f61
Show file tree
Hide file tree
Showing 6 changed files with 337 additions and 10 deletions.
291 changes: 291 additions & 0 deletions hook/src/hooks/server_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
use std::ffi::c_void;

use anyhow::Result;
use serde::{Deserialize, Serialize};

use crate::globals;
use crate::hooks::ExecFn;
use crate::ue::{self, FName, FString, TArray, TMap};

retour::static_detour! {
static GetServerName: unsafe extern "system" fn(*const c_void, *const c_void) -> *const ue::FString;
static USessionHandlingFSDFillSessionSetting: unsafe extern "system" fn(*const c_void, *mut c_void, bool, *mut c_void, *mut c_void);
}

pub fn kismet_hooks() -> &'static [(&'static str, ExecFn)] {
&[(
"/Script/FSD.SessionHandling:FSDGetModsInstalled",
exec_get_mods_installed as ExecFn,
)]
}

pub unsafe fn init_hooks() -> Result<()> {
if let Ok(server_name) = &globals().resolution.server_name {
GetServerName
.initialize(
std::mem::transmute(server_name.get_server_name.0),
detour_get_server_name,
)?
.enable()?;
}

if let Ok(server_mods) = &globals().resolution.server_mods {
USessionHandlingFSDFillSessionSetting
.initialize(
std::mem::transmute(server_mods.fill_session_setting.0),
detour_fill_session_setting,
)?
.enable()?;
}

Ok(())
}

fn detour_get_server_name(a: *const c_void, b: *const c_void) -> *const ue::FString {
unsafe {
let name = GetServerName.call(a, b).cast_mut().as_mut().unwrap();

let mut new_name = widestring::U16String::new();
new_name.push_slice([0x5b, 0x4d, 0x4f, 0x44, 0x44, 0x45, 0x44, 0x5d, 0x20, 0x0a]);
new_name.push_slice(name.as_slice());

name.clear();
name.extend_from_slice(new_name.as_slice());
name.push(0);

name
}
}

fn detour_fill_session_setting(
world: *const c_void,
game_settings: *mut c_void,
full_server: bool,
unknown1: *mut c_void,
unknown2: *mut c_void,
) {
unsafe {
USessionHandlingFSDFillSessionSetting.call(
world,
game_settings,
full_server,
unknown1,
unknown2,
);

let name = globals().meta.to_server_list_string();

let s: FString = serde_json::to_string(&vec![JsonMod {
name,
version: "mint".into(),
category: 0,
}])
.unwrap()
.as_str()
.into();

type Fn = unsafe extern "system" fn(*const c_void, ue::FName, *const ue::FString, u32);

let f: Fn = std::mem::transmute(
globals()
.resolution
.server_mods
.as_ref()
.unwrap()
.set_fstring
.0,
);

f(game_settings, ue::FName::new(&"Mods".into()), &s, 3);
}
}

#[derive(Debug, Serialize, Deserialize)]
struct JsonMod {
name: String,
version: String,
category: i32,
}

#[derive(Debug)]
#[repr(C)]
struct FBlueprintSessionResult {
online_result: FOnlineSessionSearchResult,
}
#[derive(Debug)]
#[repr(C)]
struct FOnlineSessionSearchResult {
session: FOnlineSession,
ping_in_ms: i32,
}
#[derive(Debug)]
#[repr(C)]
struct FOnlineSession {
vtable: u64,
owning_user_id: TSharedPtr, // TSharedPtr<FUniqueNetId const ,0> OwningUserId;
owning_user_name: FString,
session_settings: FOnlineSessionSettings,
session_info: TSharedPtr, //class TSharedPtr<FOnlineSessionInfo,0> SessionInfo;
num_open_private_connections: i32,
num_open_public_connections: i32,
}

#[derive(Debug)]
#[repr(C)]
struct FOnlineSessionSettings {
vtable: u64,
num_public_connections: i32,
num_private_connections: i32,
b_should_advertise: u8,
b_allow_join_in_progress: u8,
b_is_lan_match: u8,
b_is_dedicated: u8,
b_uses_stats: u8,
b_allow_invites: u8,
b_uses_presence: u8,
b_allow_join_via_presence: u8,
b_allow_join_via_presence_friends_only: u8,
b_anti_cheat_protected: u8,
build_unique_id: i32,
settings: TMap<FName, FOnlineSessionSetting>,
member_settings: [u64; 10],
}

#[derive(Debug)]
#[repr(C)]
struct FOnlineSessionSetting {
data: FVariantData,
padding: [u32; 2],
}

#[derive(Debug)]
#[repr(u32)]
#[allow(unused)]
enum EOnlineKeyValuePairDataType {
Empty,
Int32,
UInt32,
Int64,
UInt64,
Double,
String,
Float,
Blob,
Bool,
Json,
#[allow(clippy::upper_case_acronyms)]
MAX,
}

#[repr(C)]
struct FVariantData {
type_: EOnlineKeyValuePairDataType,
value: FVariantDataValue,
}
impl std::fmt::Debug for FVariantData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut dbg = f.debug_struct("FVariantData");
dbg.field("type", &self.type_);
unsafe {
match self {
Self {
type_: EOnlineKeyValuePairDataType::String,
value: FVariantDataValue { as_tchar },
} => {
dbg.field("data", &widestring::U16CStr::from_ptr_str(*as_tchar));
}
Self {
type_: EOnlineKeyValuePairDataType::UInt32,
value: FVariantDataValue { as_uint },
} => {
dbg.field("data", &as_uint);
}
Self {
type_: EOnlineKeyValuePairDataType::Int32,
value: FVariantDataValue { as_int },
} => {
dbg.field("data", &as_int);
}
_ => {
dbg.field("data", &"<unimplemented type>");
}
}
}
dbg.finish()
}
}

#[repr(C)]
union FVariantDataValue {
as_bool: bool,
as_int: i32,
as_uint: u32,
as_float: f32,
as_int64: i64,
as_uint64: u64,
as_double: f64,
as_tchar: *const u16,
as_blob: std::mem::ManuallyDrop<FVariantDataValueBlob>,
}

#[repr(C)]
struct FVariantDataValueBlob {
blob_data: *const u8,
blob_size: u32,
}

#[cfg(test)]
mod test {
use super::*;
const _: [u8; 0x20] = [0; std::mem::size_of::<FOnlineSessionSetting>()];
const _: [u8; 0x18] = [0; std::mem::size_of::<FVariantData>()];
const _: [u8; 0x10] = [0; std::mem::size_of::<FVariantDataValue>()];
const _: [u8; 0x10] = [0; std::mem::size_of::<FVariantDataValueBlob>()];
}

#[derive(Debug)]
#[repr(C)]
struct TSharedPtr {
a: u64,
b: u64,
}

unsafe extern "system" fn exec_get_mods_installed(
_context: *mut ue::UObject,
stack: *mut ue::kismet::FFrame,
result: *mut c_void,
) {
let stack = stack.as_mut().unwrap();

let session: FBlueprintSessionResult = stack.arg();
let _exclude_verified_mods: bool = stack.arg();

let result = &mut *(result as *mut TArray<FString>);
result.clear();

let settings = &session.online_result.session.session_settings.settings;

let mods = settings.find(FName::new(&"Mods".into()));
if let Some(mods) = mods {
if let FVariantData {
type_: EOnlineKeyValuePairDataType::String,
value: FVariantDataValue { as_tchar },
} = mods.data
{
if let Ok(string) = widestring::U16CStr::from_ptr_str(as_tchar).to_string() {
if let Ok(mods) = serde_json::from_str::<Vec<JsonMod>>(&string) {
for m in mods {
result.push(m.name.as_str().into());
}
}
}
}
}

// TODO figure out lifetimes of structs from kismet params
std::mem::forget(session);

if !stack.code.is_null() {
stack.code = stack.code.add(1);
}
}
3 changes: 3 additions & 0 deletions hook/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ impl Globals {
pub fn fname_to_string(&self) -> ue::FnFNameToString {
unsafe { std::mem::transmute(self.resolution.core.as_ref().unwrap().fnametostring.0) }
}
pub fn fname_ctor_wchar(&self) -> ue::FnFNameCtorWchar {
unsafe { std::mem::transmute(self.resolution.core.as_ref().unwrap().fname_ctor_wchar.0) }
}
pub fn uobject_base_utility_get_path_name(&self) -> ue::FnUObjectBaseUtilityGetPathName {
unsafe {
std::mem::transmute(
Expand Down
22 changes: 14 additions & 8 deletions hook/src/ue/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,40 @@ use crate::globals;
#[derive(Debug)]
#[repr(C)]
pub struct TArray<T> {
data: *const T,
data: *mut T,
num: i32,
max: i32,
}
impl<T> TArray<T> {
pub fn new() -> Self {
Self {
data: std::ptr::null(),
data: std::ptr::null_mut(),
num: 0,
max: 0,
}
}
pub fn as_ptr(&self) -> *const T {
self.data
}
pub fn as_mut_ptr(&mut self) -> *mut T {
self.data
}
}
impl<T> Drop for TArray<T> {
fn drop(&mut self) {
unsafe {
std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
self.data.cast_mut(),
self.data,
self.num as usize,
))
}
globals().gmalloc().free(self.data as *mut c_void);
globals().gmalloc().free(self.data.cast());
}
}
impl<T> Default for TArray<T> {
fn default() -> Self {
Self {
data: std::ptr::null(),
data: std::ptr::null_mut(),
num: 0,
max: 0,
}
Expand All @@ -44,7 +50,7 @@ impl<T> TArray<T> {
data: globals().gmalloc().malloc(
capacity * std::mem::size_of::<T>(),
std::mem::align_of::<T>() as u32,
) as *const T,
) as *mut _,
num: 0,
max: capacity as i32,
}
Expand Down Expand Up @@ -87,14 +93,14 @@ impl<T> TArray<T> {
self.data as *mut c_void,
self.max as usize * std::mem::size_of::<T>(),
std::mem::align_of::<T>() as u32,
) as *const T;
) as *mut _;
self.data = new;
}
}
pub fn push(&mut self, new_value: T) {
self.reserve(1);
unsafe {
std::ptr::write(self.data.add(self.num as usize).cast_mut(), new_value);
std::ptr::write(self.data.add(self.num as usize), new_value);
}
self.num += 1;
}
Expand Down
1 change: 1 addition & 0 deletions hook/src/ue/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub type FnFFrameStepExplicitProperty = unsafe extern "system" fn(
property: *const FProperty,
);
pub type FnFNameToString = unsafe extern "system" fn(&FName, &mut FString);
pub type FnFNameCtorWchar = unsafe extern "system" fn(&mut FName, *const u16, EFindName);

pub type FnUObjectBaseUtilityGetPathName =
unsafe extern "system" fn(&UObjectBase, Option<&UObject>, &mut FString);
Expand Down
Loading

0 comments on commit 5896f61

Please sign in to comment.