Initial commit
This commit is contained in:
166
src/lib.rs
Normal file
166
src/lib.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
mod sdk;
|
||||
|
||||
use sdk::*;
|
||||
use std::ffi::{CString, c_char, c_void};
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use libc::{dlopen, dlsym, dlclose, RTLD_LAZY, RTLD_NOLOAD, mprotect, PROT_READ, PROT_WRITE};
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
|
||||
static ENGINE: AtomicPtr<Engine> = AtomicPtr::new(ptr::null_mut());
|
||||
static CLIENT: AtomicPtr<Client> = AtomicPtr::new(ptr::null_mut());
|
||||
static PLAYER_MOVE: AtomicPtr<PlayerMove> = AtomicPtr::new(ptr::null_mut());
|
||||
|
||||
static mut ORIG_CLIENT: Option<Client> = None;
|
||||
static mut FILL_RGBA_BOX: Option<extern "C" fn(i32, i32, i32, i32, i32, i32, i32, i32)> = None;
|
||||
|
||||
struct Settings {
|
||||
show_menu: bool,
|
||||
bhop: bool,
|
||||
chams: bool,
|
||||
}
|
||||
|
||||
static mut SETTINGS: Settings = Settings {
|
||||
show_menu: false,
|
||||
bhop: true,
|
||||
chams: true,
|
||||
};
|
||||
|
||||
fn log(msg: &str) {
|
||||
if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/tmp/basturma.log") {
|
||||
let _ = writeln!(file, "[DEBUG] {}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init() {
|
||||
log("Library injected. Starting search thread...");
|
||||
std::thread::spawn(|| {
|
||||
unsafe {
|
||||
let mut hw = ptr::null_mut();
|
||||
// Список возможных имен движка в Linux
|
||||
let libs = [
|
||||
b"hw.so\0".as_ptr() as *const c_char,
|
||||
b"engine_i486.so\0".as_ptr() as *const c_char,
|
||||
b"engine.so\0".as_ptr() as *const c_char,
|
||||
b"./hw.so\0".as_ptr() as *const c_char,
|
||||
b"hw_i386.so\0".as_ptr() as *const c_char,
|
||||
];
|
||||
|
||||
log("Waiting for engine library to be loaded...");
|
||||
for i in 0..500 { // Ждем до 50 секунд
|
||||
for &lib in libs.iter() {
|
||||
hw = dlopen(lib, RTLD_LAZY | RTLD_NOLOAD);
|
||||
if !hw.is_null() {
|
||||
let lib_name = std::ffi::CStr::from_ptr(lib).to_string_lossy();
|
||||
log(&format!("Success! Found engine: {}", lib_name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !hw.is_null() { break; }
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
|
||||
if hw.is_null() {
|
||||
log("FATAL: Engine library not found after timeout!");
|
||||
return;
|
||||
}
|
||||
|
||||
setup(hw);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unsafe fn setup(hw: *mut c_void) {
|
||||
log("Starting setup and symbol lookups...");
|
||||
|
||||
FILL_RGBA_BOX = std::mem::transmute(dlsym(hw, b"Draw_FillRGBA\0".as_ptr() as *const c_char));
|
||||
if FILL_RGBA_BOX.is_some() { log("Found Draw_FillRGBA"); }
|
||||
|
||||
let engine_ptr = dlsym(hw, b"cl_enginefuncs\0".as_ptr() as *const c_char) as *mut Engine;
|
||||
if !engine_ptr.is_null() {
|
||||
ENGINE.store(engine_ptr, Ordering::SeqCst);
|
||||
log("Found cl_enginefuncs");
|
||||
} else {
|
||||
log("WARNING: cl_enginefuncs not found via dlsym");
|
||||
}
|
||||
|
||||
let client_ptr = dlsym(hw, b"cl_funcs\0".as_ptr() as *const c_char) as *mut Client;
|
||||
if !client_ptr.is_null() {
|
||||
CLIENT.store(client_ptr, Ordering::SeqCst);
|
||||
ORIG_CLIENT = Some(*client_ptr);
|
||||
|
||||
let addr = client_ptr as usize;
|
||||
let page_size = libc::sysconf(libc::_SC_PAGESIZE) as usize;
|
||||
let page_start = addr & !(page_size - 1);
|
||||
mprotect(page_start as *mut c_void, page_size, PROT_READ | PROT_WRITE);
|
||||
|
||||
(*client_ptr).createmove = hooked_createmove;
|
||||
(*client_ptr).hudredraw = hooked_hudredraw;
|
||||
(*client_ptr).hudkeyevent = hooked_hudkeyevent;
|
||||
log("Hooks applied: CreateMove, HUD_Redraw, Key_Event");
|
||||
}
|
||||
|
||||
let pmove_ptr_ptr = dlsym(hw, b"pmove\0".as_ptr() as *const c_char) as *mut *mut PlayerMove;
|
||||
if !pmove_ptr_ptr.is_null() {
|
||||
PLAYER_MOVE.store(*pmove_ptr_ptr, Ordering::SeqCst);
|
||||
log("Found pmove");
|
||||
}
|
||||
|
||||
dlclose(hw);
|
||||
log("Setup complete!");
|
||||
}
|
||||
|
||||
extern "C" fn hooked_hudkeyevent(eventcode: i32, keynum: i32, binding: *const c_char) -> i32 {
|
||||
unsafe {
|
||||
if eventcode == 1 && keynum == 146 { // INSERT
|
||||
SETTINGS.show_menu = !SETTINGS.show_menu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if SETTINGS.show_menu && eventcode == 1 {
|
||||
match keynum {
|
||||
135 => { SETTINGS.bhop = !SETTINGS.bhop; return 0; } // F1
|
||||
136 => { SETTINGS.chams = !SETTINGS.chams; return 0; } // F2
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(orig) = ORIG_CLIENT {
|
||||
(orig.hudkeyevent)(eventcode, keynum, binding)
|
||||
} else { 1 }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn hooked_hudredraw(time: f32, intermission: i32) -> i32 {
|
||||
unsafe {
|
||||
if SETTINGS.show_menu {
|
||||
if let Some(fill_box) = FILL_RGBA_BOX {
|
||||
fill_box(100, 100, 200, 100, 0, 0, 0, 200);
|
||||
let bhop_c = if SETTINGS.bhop { (0, 255, 0) } else { (255, 0, 0) };
|
||||
fill_box(110, 130, 15, 15, bhop_c.0, bhop_c.1, bhop_c.2, 255);
|
||||
let chams_c = if SETTINGS.chams { (0, 255, 0) } else { (255, 0, 0) };
|
||||
fill_box(110, 160, 15, 15, chams_c.0, chams_c.1, chams_c.2, 255);
|
||||
}
|
||||
}
|
||||
if let Some(orig) = ORIG_CLIENT { (orig.hudredraw)(time, intermission) } else { 0 }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn hooked_createmove(frametime: f32, cmd: *mut UserCmd, active: i32) {
|
||||
unsafe {
|
||||
if let Some(orig) = ORIG_CLIENT { (orig.createmove)(frametime, cmd, active); }
|
||||
let engine = ENGINE.load(Ordering::SeqCst);
|
||||
if engine.is_null() { return; }
|
||||
let lp = ((*engine).getlocalplayer)();
|
||||
if !lp.is_null() && SETTINGS.bhop { bhop(&mut *cmd); }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn bhop(cmd: &mut UserCmd) {
|
||||
if (cmd.buttons & IN_JUMP) == 0 { return; }
|
||||
let pmove = PLAYER_MOVE.load(Ordering::SeqCst);
|
||||
if pmove.is_null() { return; }
|
||||
if ((*pmove).flags & FL_ONGROUND) == 0 { cmd.buttons &= !IN_JUMP; }
|
||||
}
|
||||
Reference in New Issue
Block a user