Files
radarflow2-kvm/src/dma/context/mod.rs
Janek 00d883a41c feat: add native mode
- Add native mode
- Fix bomb holder search and wrong bomb holder on round starts
2024-04-17 18:43:21 +02:00

250 lines
9.1 KiB
Rust
Executable File

use memflow::prelude::v1::*;
mod connector;
pub use connector::Connector;
use num_traits::FromPrimitive;
use crate::{structs::Vec3, enums::TeamID};
use super::cs2dumper;
pub struct DmaCtx {
pub process: IntoProcessInstanceArcBox<'static>,
pub client_module: ModuleInfo,
pub engine_module: ModuleInfo,
}
impl DmaCtx {
fn check_version(&mut self) -> anyhow::Result<()> {
let game_build_number: u32 = self.process.read(self.engine_module.base + cs2dumper::offsets::engine2_dll::dwBuildNumber)?;
let offset_build_number = env!("CS2_BUILD_NUMBER").parse::<usize>()?;
if game_build_number as usize != offset_build_number {
return Err(anyhow::anyhow!(
"game build is {}, but offsets are for {}",
game_build_number,
offset_build_number
));
}
Ok(())
}
pub fn setup(connector: Connector, pcileech_device: String, skip_version: bool) -> anyhow::Result<DmaCtx> {
let inventory = Inventory::scan();
let os = {
if connector == Connector::Pcileech {
let args = Args::new()
.insert("device", &pcileech_device);
let connector_args = ConnectorArgs::new(None, args, None);
inventory.builder()
.connector(&connector.to_string())
.args(connector_args)
.os("win32")
.build()?
} else if connector != Connector::Native {
inventory.builder()
.connector(&connector.to_string())
.os("win32")
.build()?
} else {
memflow_native::create_os(&Default::default(), Default::default())?
}
};
let mut process = os.into_process_by_name("cs2.exe")?;
let client_module = process.module_by_name("client.dll")?;
let engine_module = process.module_by_name("engine2.dll")?;
let mut ctx = Self {
process,
client_module,
engine_module,
};
if !skip_version {
ctx.check_version()?;
}
Ok(ctx)
}
pub fn pawn_from_controller(&mut self, controller: Address, entity_list: Address) -> anyhow::Result<Option<Address>> {
let uhandle: u32 = self.process.read(controller + cs2dumper::client::CCSPlayerController::m_hPlayerPawn)?;
let list_entry = self.process.read_addr64(entity_list + 0x8 * ((uhandle & 0x7FFF) >> 9) + 16)?;
if list_entry.is_null() || !list_entry.is_valid() {
Ok(None)
} else {
let ptr = self.process.read_addr64(list_entry + 120 * (uhandle & 0x1FF))?;
Ok(Some(ptr))
}
//super::CPlayerPawn::from_uhandle(ctx, entity_list, uhandle)
}
pub fn batched_player_read(&mut self, controller: Address, pawn: Address) -> anyhow::Result<BatchedPlayerData> {
let mut pos = Vec3::default();
let mut yaw = 0f32;
let mut health = 0u32;
let mut team = 0i32;
let mut clipping_weapon = 0u64;
let mut is_scoped = 0u8;
{
let mut batcher = MemoryViewBatcher::new(&mut self.process);
batcher.read_into(pawn + cs2dumper::client::C_BasePlayerPawn::m_vOldOrigin, &mut pos);
batcher.read_into(pawn + cs2dumper::client::C_CSPlayerPawnBase::m_angEyeAngles + 4, &mut yaw);
batcher.read_into(pawn + cs2dumper::client::C_BaseEntity::m_iHealth, &mut health);
batcher.read_into(controller + cs2dumper::client::C_BaseEntity::m_iTeamNum, &mut team);
batcher.read_into(pawn + cs2dumper::client::C_CSPlayerPawnBase::m_pClippingWeapon, &mut clipping_weapon);
batcher.read_into(pawn + cs2dumper::client::C_CSPlayerPawnBase::m_bIsScoped, &mut is_scoped);
}
let team = TeamID::from_i32(team);
let has_awp = {
let clipping_weapon: Address = clipping_weapon.into();
let items_def_idx_addr = clipping_weapon + cs2dumper::client::C_EconEntity::m_AttributeManager
+ cs2dumper::client::C_AttributeContainer::m_Item + cs2dumper::client::C_EconItemView::m_iItemDefinitionIndex;
let items_def_idx: i16 = self.process.read(items_def_idx_addr)?;
items_def_idx == 9
};
Ok(BatchedPlayerData {
pos,
yaw,
team,
health,
has_awp,
is_scoped: is_scoped != 0
})
}
pub fn get_plantedc4(&mut self) -> anyhow::Result<Address> {
let ptr = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwPlantedC4)?;
let ptr2 = self.process.read_addr64(ptr)?;
Ok(ptr2)
}
/// Professionally engineered function to quickly check if the entity has class name "cs_player_controller"
pub fn is_cs_player_controller(&mut self, entity_ptr: Address) -> anyhow::Result<bool> {
let entity_identity_ptr = self.process.read_addr64(entity_ptr + cs2dumper::client::CEntityInstance::m_pEntity)?;
let class_name_ptr = self.process.read_addr64(entity_identity_ptr + cs2dumper::client::CEntityIdentity::m_designerName)?;
let data = self.process.read_raw(class_name_ptr, 20)?;
let is_controller = data == "cs_player_controller".as_bytes();
Ok(is_controller)
}
pub fn get_c4_holder(&mut self, pawns: Vec<Address>, entity_list: Address) -> Option<Address> {
// (pawn, wep_services, wep_count, wep_base)
let mut data_vec: Vec<(Address, u64, i32, u64)> = pawns
.into_iter()
.map(|pawn| (pawn, 0u64, 0i32, 0u64))
.collect();
// Get wep_services
let mut batcher = self.process.batcher();
data_vec.iter_mut().for_each(|(pawn, wep_services, _, _)| {
batcher.read_into(*pawn + cs2dumper::client::C_BasePlayerPawn::m_pWeaponServices, wep_services);
});
drop(batcher);
// Get wep_count and wep_base
let mut batcher = self.process.batcher();
data_vec.iter_mut().for_each(|(_, wep_services, wep_count, wep_base)| {
batcher.read_into((*wep_services + cs2dumper::client::CPlayer_WeaponServices::m_hMyWeapons as u64).into(), wep_count);
batcher.read_into((*wep_services + cs2dumper::client::CPlayer_WeaponServices::m_hMyWeapons as u64 + 0x8).into() , wep_base);
});
drop(batcher);
// Rebuild data vec
// Vec<(pawn, wep_base, Vec<(buff, buff2)>)>
let mut data_vec: Vec<(Address, u64, Vec<(u64, i32)>)> = data_vec
.into_iter()
.map(|(pawn, _, wep_count, wep_base)| {
let weps = (0..wep_count).into_iter().map(|idx| (0u64, idx)).collect();
(pawn, wep_base, weps)
})
.collect();
// Get handle
let mut batcher = self.process.batcher();
data_vec.iter_mut().for_each(|(_, wep_base, wep_data_vec)| {
wep_data_vec.iter_mut().for_each(|(_, idx)| {
let b: Address = (*wep_base).into();
batcher.read_into(b + * idx * 0x4, idx);
});
});
drop(batcher);
// Get list entry
let mut batcher = self.process.batcher();
data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
wep_data_vec.iter_mut().for_each(|(list_entry, handle)| {
batcher.read_into(entity_list + 0x8 * ((*handle & 0x7FFF) >> 9) + 16, list_entry);
});
});
drop(batcher);
// Get wep ptr
let mut batcher = self.process.batcher();
data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
wep_data_vec.iter_mut().for_each(|(list_entry, handle)| {
let le: Address = (*list_entry).into();
batcher.read_into(le + 120 * (*handle & 0x1FF), list_entry);
});
});
drop(batcher);
// Get wep data
let mut batcher = self.process.batcher();
data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
wep_data_vec.iter_mut().for_each(|(wep_ptr, _)| {
let b: Address = (*wep_ptr).into();
batcher.read_into(b + cs2dumper::client::C_BaseEntity::m_nSubclassID + 0x8, wep_ptr);
});
});
drop(batcher);
// Get wep id
let mut batcher = self.process.batcher();
data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
wep_data_vec.iter_mut().for_each(|(wep_data, id)| {
let b: Address = (*wep_data).into();
batcher.read_into(b + cs2dumper::client::CCSWeaponBaseVData::m_WeaponType, id);
});
});
drop(batcher);
let holder = data_vec.into_iter().find(|(_, _, wep_data_vec)| {
wep_data_vec.iter().find(|(_, id)| { *id == 7 }).is_some()
});
match holder {
Some((addr, _, _)) => Some(addr),
None => None,
}
}
}
#[derive(Debug)]
pub struct BatchedPlayerData {
pub pos: Vec3,
pub yaw: f32,
pub team: Option<TeamID>,
pub health: u32,
pub has_awp: bool,
pub is_scoped: bool,
}