|
| 1 | +use std::ffi::c_void; |
| 2 | + |
| 3 | +use retour::GenericDetour; |
| 4 | + |
| 5 | +use crate::config::Config; |
| 6 | +use crate::mod_api; |
| 7 | +use crate::overlay; |
| 8 | + |
| 9 | +// IDirect3DDevice9 vtable index |
| 10 | +const PRESENT_INDEX: usize = 17; |
| 11 | +const END_SCENE_INDEX: usize = 42; |
| 12 | + |
| 13 | +type PresentFn = unsafe extern "system" fn( |
| 14 | + *mut c_void, *const c_void, *const c_void, usize, *const c_void, |
| 15 | +) -> i32; |
| 16 | +type EndSceneFn = unsafe extern "system" fn(*mut c_void) -> i32; |
| 17 | + |
| 18 | +pub static mut DEVICE_CONFIG: Option<Config> = None; |
| 19 | +pub static mut FPS_STATE: Option<overlay::FpsState> = None; |
| 20 | +pub static mut GAME_HWND: usize = 0; |
| 21 | + |
| 22 | +static mut PRESENT_HOOK: Option<GenericDetour<PresentFn>> = None; |
| 23 | +static mut END_SCENE_HOOK: Option<GenericDetour<EndSceneFn>> = None; |
| 24 | + |
| 25 | +pub unsafe fn patch(device: *mut c_void, mut config: Config) { |
| 26 | + mod_api::apply_pending(&mut config); |
| 27 | + let frame_lock = config.frame_lock; |
| 28 | + DEVICE_CONFIG = Some(config); |
| 29 | + if frame_lock.is_some() { |
| 30 | + FPS_STATE = Some(overlay::FpsState::new()); |
| 31 | + } |
| 32 | + mod_api::set_device(device); |
| 33 | + |
| 34 | + let vtable = *(device as *const *const usize); |
| 35 | + let present_addr = *vtable.add(PRESENT_INDEX); |
| 36 | + let end_scene_addr = *vtable.add(END_SCENE_INDEX); |
| 37 | + |
| 38 | + let present_fn: PresentFn = std::mem::transmute(present_addr); |
| 39 | + if let Ok(hook) = GenericDetour::<PresentFn>::new(present_fn, hooked_present) { |
| 40 | + let _ = hook.enable(); |
| 41 | + PRESENT_HOOK = Some(hook); |
| 42 | + } |
| 43 | + |
| 44 | + let end_scene_fn: EndSceneFn = std::mem::transmute(end_scene_addr); |
| 45 | + if let Ok(hook) = GenericDetour::<EndSceneFn>::new(end_scene_fn, hooked_end_scene) { |
| 46 | + let _ = hook.enable(); |
| 47 | + END_SCENE_HOOK = Some(hook); |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +// IDirect3DDevice9::Present(RECT*, RECT*, HWND, RGNDATA*) |
| 52 | +unsafe extern "system" fn hooked_present( |
| 53 | + this: *mut c_void, |
| 54 | + source_rect: *const c_void, |
| 55 | + dest_rect: *const c_void, |
| 56 | + dest_window_override: usize, |
| 57 | + dirty_region: *const c_void, |
| 58 | +) -> i32 { |
| 59 | + if let Some(state) = &mut FPS_STATE { |
| 60 | + if let Some(cfg) = &DEVICE_CONFIG { |
| 61 | + if let Some(target_fps) = cfg.frame_lock { |
| 62 | + state.frame_lock(target_fps); |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + PRESENT_HOOK.as_ref().unwrap().call(this, source_rect, dest_rect, dest_window_override, dirty_region) |
| 68 | +} |
| 69 | + |
| 70 | +// IDirect3DDevice9::EndScene() |
| 71 | +unsafe extern "system" fn hooked_end_scene(this: *mut c_void) -> i32 { |
| 72 | + mod_api::run_present_callbacks(this); |
| 73 | + |
| 74 | + END_SCENE_HOOK.as_ref().unwrap().call(this) |
| 75 | +} |
0 commit comments