diff --git a/Cargo.lock b/Cargo.lock index 53ba8fe3..b5c33126 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2196,6 +2196,7 @@ dependencies = [ "serde_json", "tokio", "tracing", + "tracing-appender", "widestring", "windows 0.52.0", ] @@ -3290,7 +3291,7 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" [[package]] name = "patternsleuth" version = "0.1.0" -source = "git+https://github.com/trumank/patternsleuth#0d5175345a4827f98bc51df893cf2079ebf53306" +source = "git+https://github.com/trumank/patternsleuth#ac36038da71fcb69b79af4506f332b798e87cdcd" dependencies = [ "anyhow", "futures", @@ -3311,7 +3312,7 @@ dependencies = [ [[package]] name = "patternsleuth_scanner" version = "0.1.0" -source = "git+https://github.com/trumank/patternsleuth#0d5175345a4827f98bc51df893cf2079ebf53306" +source = "git+https://github.com/trumank/patternsleuth#ac36038da71fcb69b79af4506f332b798e87cdcd" dependencies = [ "anyhow", "memchr", diff --git a/hook/Cargo.toml b/hook/Cargo.toml index da9f14d5..938a0d94 100644 --- a/hook/Cargo.toml +++ b/hook/Cargo.toml @@ -32,3 +32,4 @@ mint_lib = { path = "../mint_lib" } bitflags = "2.4.1" widestring = "1.0.2" tokio = { workspace = true, features = ["full"] } +tracing-appender = "0.2.3" diff --git a/hook/src/hooks/mod.rs b/hook/src/hooks/mod.rs index 33e59a86..3a948872 100644 --- a/hook/src/hooks/mod.rs +++ b/hook/src/hooks/mod.rs @@ -18,6 +18,7 @@ use windows::Win32::System::Memory::{VirtualProtect, PAGE_EXECUTE_READWRITE}; use crate::{ globals, ue::{self, FLinearColor, UObject}, + LOG_GUARD, }; retour::static_detour! { @@ -26,6 +27,8 @@ retour::static_detour! { static LoadGameFromSlot: unsafe extern "system" fn(*const ue::FString, i32) -> *const USaveGame; static DoesSaveGameExist: unsafe extern "system" fn(*const ue::FString, i32) -> bool; static UObjectTemperatureComponentTimerCallback: unsafe extern "system" fn(*mut c_void); + static WinMain: unsafe extern "system" fn(*mut (), *mut (), *mut (), i32, *const ()) -> i32; + } #[repr(C)] @@ -54,6 +57,12 @@ pub unsafe fn initialize() -> Result<()> { .cloned() .collect::>(); + WinMain.initialize( + std::mem::transmute(globals().resolution.core.as_ref().unwrap().main.0), + detour_main, + )?; + WinMain.enable()?; + HookUFunctionBind.initialize( std::mem::transmute(globals().resolution.core.as_ref().unwrap().ufunction_bind.0), move |function| { @@ -272,6 +281,29 @@ fn does_save_game_exist_detour(slot_name: *const ue::FString, user_index: i32) - } } +fn detour_main( + h_instance: *mut (), + h_prev_instance: *mut (), + lp_cmd_line: *mut (), + n_cmd_show: i32, + cmd_line: *const (), +) -> i32 { + let ret = unsafe { + WinMain.call( + h_instance, + h_prev_instance, + lp_cmd_line, + n_cmd_show, + cmd_line, + ) + }; + + // about to exit, drop log guard + drop(unsafe { LOG_GUARD.take() }); + + ret +} + unsafe extern "system" fn exec_get_mod_json( _context: *mut ue::UObject, stack: *mut ue::kismet::FFrame, diff --git a/hook/src/lib.rs b/hook/src/lib.rs index 3dc9068a..7a6b28bd 100644 --- a/hook/src/lib.rs +++ b/hook/src/lib.rs @@ -53,6 +53,7 @@ unsafe extern "system" fn init(_: usize) { } static mut GLOBALS: Option = None; +static mut LOG_GUARD: Option = None; pub struct Globals { resolution: hook_resolvers::HookResolution, @@ -137,10 +138,6 @@ unsafe fn patch() -> Result<()> { if guard.is_none() { warn!("failed to set up logging"); } - // Normally this guard should be held in the main scope so it's dropped on exit, but since we - // don't control the entrypoint we just leak it so it doesn't get dropped early. In the future - // the UE exit could be hooked to drop the guard there instead. - std::mem::forget(guard); let pak_path = bin_dir .and_then(Path::parent) @@ -159,6 +156,7 @@ unsafe fn patch() -> Result<()> { info!("PS scan: {:#x?}", resolution); GLOBALS = Some(Globals { resolution, meta }); + LOG_GUARD = guard; hooks::initialize()?; diff --git a/hook_resolvers/src/lib.rs b/hook_resolvers/src/lib.rs index 6ff07a5d..85b42678 100644 --- a/hook_resolvers/src/lib.rs +++ b/hook_resolvers/src/lib.rs @@ -1,6 +1,7 @@ use patternsleuth::resolvers::futures::future::join_all; use patternsleuth::resolvers::unreal::blueprint_library::UFunctionBind; use patternsleuth::resolvers::unreal::fname::{FNameCtorWchar, FNameToString}; +use patternsleuth::resolvers::unreal::game_loop::Main; use patternsleuth::resolvers::unreal::gmalloc::GMalloc; use patternsleuth::resolvers::unreal::kismet::{FFrameStep, FFrameStepExplicitProperty}; use patternsleuth::resolvers::unreal::save_game::{ @@ -205,6 +206,7 @@ impl_try_collector! { )] pub struct CoreResolution { pub gmalloc: GMalloc, + pub main: Main, pub fnametostring: FNameToString, pub fname_ctor_wchar: FNameCtorWchar, pub uobject_base_utility_get_path_name: UObjectBaseUtilityGetPathName,