diff --git a/src/hooks/engine.rs b/src/hooks/engine.rs index cdf3380..55f13d1 100644 --- a/src/hooks/engine.rs +++ b/src/hooks/engine.rs @@ -99,6 +99,7 @@ pub static CL_GameDir_f: Pointer = Pointer::empty_patter ]), null_mut(), ); +pub static cl_lightstyle: Pointer<*mut [lightstyle_t; 64]> = Pointer::empty(b"cl_lightstyle\0"); pub static CL_Move: Pointer = Pointer::empty_patterns( b"CL_Move\0", // To find, search for "Client Move". @@ -108,6 +109,15 @@ pub static CL_Move: Pointer = Pointer::empty_patterns( ]), my_CL_Move as _, ); +pub static CL_Parse_LightStyle: Pointer = Pointer::empty_patterns( + b"CL_Parse_LightStyle\0", + // To find, search for "svc_lightstyle > MAX_LIGHTSTYLES" + Patterns(&[ + // 8684 + pattern!(56 57 E8 ?? ?? ?? ?? 8B ?? 83 ?? ?? ?? ?? 68), + ]), + my_CL_Parse_LightStyle as _, +); pub static CL_PlayDemo_f: Pointer = Pointer::empty_patterns( b"CL_PlayDemo_f\0", // To find, search for "playdemo : plays a demo". @@ -908,7 +918,9 @@ static POINTERS: &[&dyn PointerTrait] = &[ &CL_Disconnect, &cl_funcs, &CL_GameDir_f, + &cl_lightstyle, &CL_Move, + &CL_Parse_LightStyle, &CL_PlayDemo_f, &CL_ViewDemo_f, &ClientDLL_Init, @@ -1134,6 +1146,13 @@ pub struct client_static_s_demos { pub demoplayback: c_int, } +#[repr(C)] +#[derive(Clone, Copy)] +pub struct lightstyle_t { + pub length: c_int, + pub map: [c_char; 64], +} + #[repr(C)] pub struct movevars_s { pub gravity: c_float, @@ -1442,6 +1461,13 @@ pub unsafe fn find_pointers(marker: MainThreadMarker, base: *mut c_void, size: u _ => (), } + let ptr = &CL_Parse_LightStyle; + match ptr.pattern_index(marker) { + // 8684 + Some(0) => cl_lightstyle.set(marker, ptr.by_offset(marker, 75)), + _ => (), + } + let ptr = &ClientDLL_Init; match ptr.pattern_index(marker) { // 6153 @@ -2422,6 +2448,17 @@ pub mod exported { }) } + #[export_name = "CL_Parse_LightStyle"] + pub unsafe extern "C" fn my_CL_Parse_LightStyle() { + abort_on_panic(move || { + let marker = MainThreadMarker::new(); + + CL_Parse_LightStyle.get(marker)(); + + lightstyle::on_cl_parse_lightstyle(marker); + }) + } + #[export_name = "CL_PlayDemo_f"] pub unsafe extern "C" fn my_CL_PlayDemo_f() { abort_on_panic(move || { diff --git a/src/modules/lightstyle.rs b/src/modules/lightstyle.rs new file mode 100644 index 0000000..6f594b3 --- /dev/null +++ b/src/modules/lightstyle.rs @@ -0,0 +1,142 @@ +//! `bxt_lightstyle` + +use byte_slice_cast::AsSliceOf; + +use super::commands::Command; +use super::Module; +use crate::handler; +use crate::hooks::engine; +use crate::modules::cvars::CVar; +use crate::utils::*; + +pub struct LightStyle; +impl Module for LightStyle { + fn name(&self) -> &'static str { + "bxt_lightstyle" + } + + fn description(&self) -> &'static str { + "Change rendering light styles." + } + + fn commands(&self) -> &'static [&'static Command] { + static COMMANDS: &[&Command] = &[&BXT_LIGHTSTYLE_APPLY]; + COMMANDS + } + + fn cvars(&self) -> &'static [&'static CVar] { + static CVARS: &[&CVar] = &[&BXT_LIGHTSTYLE_CUSTOM, &BXT_LIGHTSTYLE]; + CVARS + } + + fn is_enabled(&self, marker: MainThreadMarker) -> bool { + engine::cl_lightstyle.is_set(marker) && engine::CL_Parse_LightStyle.is_set(marker) + } +} + +static BXT_LIGHTSTYLE: CVar = CVar::new( + b"bxt_lightstyle\0", + b"0\0", + "\ +Preset controls. Must invoke apply command to take effects. Persists across level changes. + +0: Off +1: Maximum brightness +2: Full bright +3: Maximum darkness +4: Mildy darker", +); +static BXT_LIGHTSTYLE_CUSTOM: CVar = CVar::new( + b"bxt_lightstyle_custom\0", + b"\0", + "\ +Custom controls. Takes precedence over preset when using bxt_lightstyle_apply. + +First value is effect. Second value is amount. +E.g.: bxt_lightstyle_custom \"1 nomnomnom\". +", +); + +static BXT_LIGHTSTYLE_APPLY: Command = Command::new( + b"bxt_lightstyle_apply\0", + handler!( + "Apply lightstyle changes. Takes an optional argument for instantly applying a preset.", + apply_from_cvars as fn(_), + apply_preset as fn(_, _) + ), +); + +static ORIGINAL_LIGHTSTYLE: MainThreadRefCell> = MainThreadRefCell::new(vec![]); + +fn apply_from_cvars(marker: MainThreadMarker) { + let input = BXT_LIGHTSTYLE_CUSTOM.to_string(marker); + + if !input.is_empty() { + // 0 and "m" is default normal + let mut args = input.split_ascii_whitespace(); + let index = args.next().and_then(|x| x.parse().ok()).unwrap_or(0); + let lightinfo = args.next().unwrap_or("m"); + + apply(marker, index, lightinfo); + } else { + apply_preset(marker, BXT_LIGHTSTYLE.as_u64(marker) as usize) + } +} + +fn apply_preset(marker: MainThreadMarker, preset: usize) { + let lightinfo = match preset { + 0 => "", + 1 => "z", + 2 => "#", + 3 => "a", + 4 => "g", // from someone else's personal preference + _ => "m", // m is the default normal lighting + }; + + apply(marker, 0, lightinfo); +} + +fn apply(marker: MainThreadMarker, index: usize, lightinfo: &str) { + if !LightStyle.is_enabled(marker) { + return; + } + + if index > 63 { + return; + } + + if lightinfo.len() > 64 { + return; + } + + unsafe { + let cl_lightstyle = &mut *engine::cl_lightstyle.get(marker); + let original = ORIGINAL_LIGHTSTYLE.borrow_mut(marker); + + let slice: &[i8] = if lightinfo.is_empty() && !(*original).is_empty() && index == 0 { + (*original).as_slice() + } else { + lightinfo.as_slice_of().unwrap() + }; + + let slice_len = slice.len(); + + cl_lightstyle[index].map[..slice_len].copy_from_slice(slice); + cl_lightstyle[index].length = slice_len as i32; + } +} + +pub fn on_cl_parse_lightstyle(marker: MainThreadMarker) { + // It is possible that the map has a preferred light style. + // Then, if we don't have any thing for our cvar, which is style is normal + // and no custom. THen we just don't do anything. + if BXT_LIGHTSTYLE.as_u64(marker) != 0 || !BXT_LIGHTSTYLE_CUSTOM.to_string(marker).is_empty() { + { + let cl_lightstyle = &mut unsafe { *engine::cl_lightstyle.get(marker) }; + // More often a map's default lightstyle will be empty. + *ORIGINAL_LIGHTSTYLE.borrow_mut(marker) = cl_lightstyle[0].map.to_vec(); + } + + apply_from_cvars(marker); + } +} diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 1416ce8..7de59c2 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -34,6 +34,7 @@ pub mod force_fov; pub mod help; pub mod hud; pub mod hud_scale; +pub mod lightstyle; pub mod novis; pub mod player_movement_tracing; pub mod remote_forbid; @@ -100,6 +101,7 @@ pub static MODULES: &[&dyn Module] = &[ &help::Help, &hud::Hud, &hud_scale::HudScale, + &lightstyle::LightStyle, &novis::NoVis, &player_movement_tracing::PlayerMovementTracing, &remote_forbid::RemoteForbid,