mirror of
https://github.com/a2x/cs2-dumper.git
synced 2025-10-07 13:20:04 +08:00
Add YAML file builder and automatically resolve offsets in tests
This commit is contained in:
@@ -43,8 +43,8 @@ impl FileBuilder for JsonFileBuilder {
|
||||
name: &str,
|
||||
comment: Option<&str>,
|
||||
) -> Result<()> {
|
||||
self.data.entry(name.to_string()).or_default().comment = comment.map(str::to_string);
|
||||
self.current_namespace = name.to_string();
|
||||
self.data.entry(name.to_string()).or_default().comment = comment.map(str::to_string);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ pub use file_builder::FileBuilder;
|
||||
pub use json_file_builder::JsonFileBuilder;
|
||||
pub use python_file_builder::PythonFileBuilder;
|
||||
pub use rust_file_builder::RustFileBuilder;
|
||||
pub use yaml_file_builder::YamlFileBuilder;
|
||||
|
||||
pub use std::io::{Result, Write};
|
||||
|
||||
@@ -13,6 +14,7 @@ pub mod file_builder;
|
||||
pub mod json_file_builder;
|
||||
pub mod python_file_builder;
|
||||
pub mod rust_file_builder;
|
||||
pub mod yaml_file_builder;
|
||||
|
||||
/// `FileBuilder` is an enum that defines different kinds of file builders.
|
||||
/// Each variant corresponds to a builder for a particular type of file.
|
||||
@@ -32,6 +34,9 @@ pub enum FileBuilderEnum {
|
||||
|
||||
/// Represents a builder for Rust files.
|
||||
RustFileBuilder(RustFileBuilder),
|
||||
|
||||
/// Represents a builder for YAML files.
|
||||
YamlFileBuilder(YamlFileBuilder),
|
||||
}
|
||||
|
||||
impl FileBuilder for FileBuilderEnum {
|
||||
@@ -77,6 +82,7 @@ impl FileBuilderEnum {
|
||||
FileBuilderEnum::JsonFileBuilder(builder) => builder,
|
||||
FileBuilderEnum::PythonFileBuilder(builder) => builder,
|
||||
FileBuilderEnum::RustFileBuilder(builder) => builder,
|
||||
FileBuilderEnum::YamlFileBuilder(builder) => builder,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
48
src/builder/yaml_file_builder.rs
Normal file
48
src/builder/yaml_file_builder.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use super::FileBuilder;
|
||||
|
||||
use std::io::{Result, Write};
|
||||
|
||||
/// A structure representing a builder for Yaml files.
|
||||
/// The builder implements the `FileBuilder` trait.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct YamlFileBuilder;
|
||||
|
||||
impl FileBuilder for YamlFileBuilder {
|
||||
fn extension(&mut self) -> &str {
|
||||
"yaml"
|
||||
}
|
||||
|
||||
fn write_top_level(&mut self, output: &mut dyn Write) -> Result<()> {
|
||||
write!(output, "---\n")
|
||||
}
|
||||
|
||||
fn write_namespace(
|
||||
&mut self,
|
||||
output: &mut dyn Write,
|
||||
name: &str,
|
||||
comment: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let comment = comment.map_or(String::new(), |c| format!(" # {}", c));
|
||||
|
||||
write!(output, "{}:{}\n", name, comment)
|
||||
}
|
||||
|
||||
fn write_variable(
|
||||
&mut self,
|
||||
output: &mut dyn Write,
|
||||
name: &str,
|
||||
value: usize,
|
||||
comment: Option<&str>,
|
||||
indentation: Option<usize>,
|
||||
) -> Result<()> {
|
||||
let indentation = " ".repeat(indentation.unwrap_or(4));
|
||||
|
||||
let comment = comment.map_or(String::new(), |c| format!(" # {}", c));
|
||||
|
||||
write!(output, "{}{}: {}{}\n", indentation, name, value, comment)
|
||||
}
|
||||
|
||||
fn write_closure(&mut self, _output: &mut dyn Write, _eof: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -14,7 +14,7 @@ use std::mem::offset_of;
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct InterfaceNode {
|
||||
/// Function pointer used to instantiate an instance of the interface.
|
||||
/// Used to instantiate an instance of the interface.
|
||||
pub create_fn: *const (),
|
||||
|
||||
/// Pointer to the name of the interface.
|
||||
@@ -43,7 +43,7 @@ impl InterfaceNode {
|
||||
.map(|ptr| ptr.into())
|
||||
}
|
||||
|
||||
/// Returns the name of the interface.
|
||||
/// Returns the name of the interface with the version number appended.
|
||||
///
|
||||
/// E.g. "Source2Client002".
|
||||
///
|
||||
@@ -100,6 +100,7 @@ pub fn dump_interfaces(
|
||||
) -> Result<()> {
|
||||
let mut entries = Entries::new();
|
||||
|
||||
// Iterate over all modules in the process, excluding crashhandler64.dll.
|
||||
for module in process
|
||||
.modules()?
|
||||
.iter()
|
||||
@@ -113,6 +114,7 @@ pub fn dump_interfaces(
|
||||
|
||||
let mut node = process.read_memory::<*mut InterfaceNode>(create_interface_address)?;
|
||||
|
||||
// Iterate over each node in the linked list.
|
||||
while !node.is_null() {
|
||||
let instance = unsafe { (*node).instance(process) }?;
|
||||
let name = unsafe { (*node).name(process) }?;
|
||||
@@ -136,6 +138,7 @@ pub fn dump_interfaces(
|
||||
indent: Some(indent),
|
||||
});
|
||||
|
||||
// Get the next node in the linked list.
|
||||
node = unsafe { (*node).next(process) }?;
|
||||
}
|
||||
}
|
||||
|
@@ -140,6 +140,7 @@ fn write_banner_to_file(file: &mut File, file_extension: &str) -> Result<()> {
|
||||
"'''\nCreated using {}\n{}\n'''\n\n",
|
||||
REPO_URL, time_now
|
||||
)),
|
||||
"yaml" => None,
|
||||
_ => Some(format!(
|
||||
"/*\n * Created using {}\n * {}\n */\n\n",
|
||||
REPO_URL, time_now
|
||||
|
@@ -136,7 +136,10 @@ mod tests {
|
||||
|
||||
use core::arch::x86_64::_bittest;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use std::ffi::{c_char, c_void};
|
||||
use std::fs;
|
||||
use std::mem::offset_of;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
@@ -149,6 +152,60 @@ mod tests {
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
fn get_class_field(module_name: &str, class_name: &str, class_key: &str) -> Option<u64> {
|
||||
let content = match fs::read_to_string(format!("generated/{}.json", module_name)) {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
eprintln!("Error reading file: {}", e);
|
||||
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let value: Value = match serde_json::from_str(&content) {
|
||||
Ok(parsed_value) => parsed_value,
|
||||
Err(e) => {
|
||||
eprintln!("Error parsing JSON: {}", e);
|
||||
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let class_value = &value[class_name];
|
||||
let class_data = &class_value["data"];
|
||||
let class_key_value = &class_data[class_key];
|
||||
let value = class_key_value["value"].as_u64();
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
fn get_offset_value(module_name: &str, offset_name: &str) -> Option<u64> {
|
||||
let content = match fs::read_to_string("generated/offsets.json") {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
eprintln!("Error reading file: {}", e);
|
||||
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let value: Value = match serde_json::from_str(&content) {
|
||||
Ok(parsed_value) => parsed_value,
|
||||
Err(e) => {
|
||||
eprintln!("Error parsing JSON: {}", e);
|
||||
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let class_value = &value[module_name.replace(".", "_")];
|
||||
let class_data = &class_value["data"];
|
||||
let class_key_value = &class_data[offset_name];
|
||||
let value = class_key_value["value"].as_u64();
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_number() -> Result<()> {
|
||||
let process = setup()?;
|
||||
@@ -158,7 +215,11 @@ mod tests {
|
||||
.expect("Failed to find engine2.dll")
|
||||
.base();
|
||||
|
||||
let build_number = process.read_memory::<u32>(engine_base + 0x48B344)?; // dwBuildNumber
|
||||
let build_number_offset = get_offset_value("engine2.dll", "dwBuildNumber")
|
||||
.expect("Failed to find dwBuildNumber offset");
|
||||
|
||||
let build_number =
|
||||
process.read_memory::<u32>(engine_base + build_number_offset as usize)?;
|
||||
|
||||
println!("Build number: {}", build_number);
|
||||
|
||||
@@ -174,14 +235,16 @@ mod tests {
|
||||
.expect("Failed to find client.dll")
|
||||
.base();
|
||||
|
||||
let force_attack = process.read_memory::<u32>(client_base + 0x16BBD30)?; // dwForceAttack
|
||||
let force_attack_2 = process.read_memory::<u32>(client_base + 0x16BBDC0)?; // dwForceAttack2
|
||||
let force_backward = process.read_memory::<u32>(client_base + 0x16BC000)?; // dwForceBackward
|
||||
let force_crouch = process.read_memory::<u32>(client_base + 0x16BC2D0)?; // dwForceCrouch
|
||||
let force_forward = process.read_memory::<u32>(client_base + 0x16BBF70)?; // dwForceForward
|
||||
let force_jump = process.read_memory::<u32>(client_base + 0x16BC240)?; // dwForceJump
|
||||
let force_left = process.read_memory::<u32>(client_base + 0x16BC090)?; // dwForceLeft
|
||||
let force_right = process.read_memory::<u32>(client_base + 0x16BC120)?; // dwForceRight
|
||||
const BUTTONS: [&str; 8] = [
|
||||
"dwForceAttack",
|
||||
"dwForceAttack2",
|
||||
"dwForceBackward",
|
||||
"dwForceCrouch",
|
||||
"dwForceForward",
|
||||
"dwForceJump",
|
||||
"dwForceLeft",
|
||||
"dwForceRight",
|
||||
];
|
||||
|
||||
let get_key_state = |value: u32| -> &str {
|
||||
match value {
|
||||
@@ -191,14 +254,17 @@ mod tests {
|
||||
}
|
||||
};
|
||||
|
||||
println!("Force attack: {}", get_key_state(force_attack));
|
||||
println!("Force attack 2: {}", get_key_state(force_attack_2));
|
||||
println!("Force backward: {}", get_key_state(force_backward));
|
||||
println!("Force crouch: {}", get_key_state(force_crouch));
|
||||
println!("Force forward: {}", get_key_state(force_forward));
|
||||
println!("Force jump: {}", get_key_state(force_jump));
|
||||
println!("Force left: {}", get_key_state(force_left));
|
||||
println!("Force right: {}", get_key_state(force_right));
|
||||
// Sleep for a second so we're able to test.
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
for button in &BUTTONS {
|
||||
let offset = get_offset_value("client.dll", button)
|
||||
.expect(&format!("Failed to find {} offset", button));
|
||||
|
||||
let value = process.read_memory::<u32>(client_base + offset as usize)?;
|
||||
|
||||
println!("{}: {}", button, get_key_state(value));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -251,7 +317,11 @@ mod tests {
|
||||
.expect("Failed to find client.dll")
|
||||
.base();
|
||||
|
||||
let global_vars = process.read_memory::<*const GlobalVarsBase>(client_base + 0x16B7EA0)?; // dwGlobalVars
|
||||
let global_vars_offset = get_offset_value("client.dll", "dwGlobalVars")
|
||||
.expect("Failed to find dwGlobalVars offset");
|
||||
|
||||
let global_vars = process
|
||||
.read_memory::<*const GlobalVarsBase>(client_base + global_vars_offset as usize)?;
|
||||
|
||||
let current_map_name = unsafe {
|
||||
(*global_vars)
|
||||
@@ -273,7 +343,10 @@ mod tests {
|
||||
.expect("Failed to find inputsystem.dll")
|
||||
.base();
|
||||
|
||||
let input_system = input_system_base + 0x35760; // dwInputSystem
|
||||
let input_system_offset = get_offset_value("inputsystem.dll", "dwInputSystem")
|
||||
.expect("Failed to find dwInputSystem offset");
|
||||
|
||||
let input_system = input_system_base + input_system_offset as usize;
|
||||
|
||||
let is_key_down = |key_code: i32| -> bool {
|
||||
let key_map_element = process
|
||||
@@ -283,10 +356,11 @@ mod tests {
|
||||
unsafe { _bittest(&key_map_element, key_code & 0x1F) != 0 }
|
||||
};
|
||||
|
||||
// Sleep for a second so we're able to test.
|
||||
sleep(Duration::from_secs(1));
|
||||
|
||||
// See https://www.unknowncheats.me/forum/3855779-post889.html for button codes.
|
||||
println!("Insert down: {}", is_key_down(73));
|
||||
println!("INSERT down: {}", is_key_down(73));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -300,9 +374,19 @@ mod tests {
|
||||
.expect("Failed to find client.dll")
|
||||
.base();
|
||||
|
||||
let local_player_controller = process.read_memory::<usize>(client_base + 0x180ACB0)?; // dwLocalPlayerController
|
||||
let local_player_controller_offset =
|
||||
get_offset_value("client.dll", "dwLocalPlayerController")
|
||||
.expect("Failed to find dwLocalPlayerController offset");
|
||||
|
||||
let player_name = process.read_string((local_player_controller + 0x640).into())?; // m_iszPlayerName
|
||||
let player_name_offset =
|
||||
get_class_field("client.dll", "CBasePlayerController", "m_iszPlayerName")
|
||||
.expect("Failed to find m_iszPlayerName offset");
|
||||
|
||||
let local_player_controller =
|
||||
process.read_memory::<usize>(client_base + local_player_controller_offset as usize)?;
|
||||
|
||||
let player_name =
|
||||
process.read_string((local_player_controller + player_name_offset as usize).into())?;
|
||||
|
||||
println!("Local player name: {}", player_name);
|
||||
|
||||
@@ -311,17 +395,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn local_player_pawn() -> Result<()> {
|
||||
let process = setup()?;
|
||||
|
||||
let client_base = process
|
||||
.get_module_by_name("client.dll")
|
||||
.expect("Failed to find client.dll")
|
||||
.base();
|
||||
|
||||
let local_player_pawn = process.read_memory::<usize>(client_base + 0x16C2DD8)?; // dwLocalPlayerPawn
|
||||
|
||||
let game_scene_node = process.read_memory::<usize>((local_player_pawn + 0x310).into())?; // m_pGameSceneNode
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct Vector3D {
|
||||
@@ -330,9 +403,34 @@ mod tests {
|
||||
z: f32,
|
||||
}
|
||||
|
||||
let origin = process.read_memory::<Vector3D>((game_scene_node + 0xC8).into())?; // m_vecAbsOrigin
|
||||
let process = setup()?;
|
||||
|
||||
println!("Local player origin: {:?}", origin);
|
||||
let client_base = process
|
||||
.get_module_by_name("client.dll")
|
||||
.expect("Failed to find client.dll")
|
||||
.base();
|
||||
|
||||
let local_player_pawn_offset = get_offset_value("client.dll", "dwLocalPlayerPawn")
|
||||
.expect("Failed to find dwLocalPlayerPawn offset");
|
||||
|
||||
let game_scene_node_offset =
|
||||
get_class_field("client.dll", "C_BaseEntity", "m_pGameSceneNode")
|
||||
.expect("Failed to find m_pGameSceneNode offset");
|
||||
|
||||
let absolute_origin_offset =
|
||||
get_class_field("client.dll", "CGameSceneNode", "m_vecAbsOrigin")
|
||||
.expect("Failed to find m_vecAbsOrigin offset");
|
||||
|
||||
let local_player_pawn =
|
||||
process.read_memory::<usize>(client_base + local_player_pawn_offset as usize)?;
|
||||
|
||||
let game_scene_node = process
|
||||
.read_memory::<usize>((local_player_pawn + game_scene_node_offset as usize).into())?;
|
||||
|
||||
let absolute_origin = process
|
||||
.read_memory::<Vector3D>((game_scene_node + absolute_origin_offset as usize).into())?;
|
||||
|
||||
println!("Local player origin: {:?}", absolute_origin);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -346,8 +444,17 @@ mod tests {
|
||||
.expect("Failed to find engine2.dll")
|
||||
.base();
|
||||
|
||||
let window_width = process.read_memory::<u32>(engine_base + 0x541A68)?; // dwWindowWidth
|
||||
let window_height = process.read_memory::<u32>(engine_base + 0x541A6C)?; // dwWindowHeight
|
||||
let window_width_offset = get_offset_value("engine2.dll", "dwWindowWidth")
|
||||
.expect("Failed to find dwLocalPlayerPawn offset");
|
||||
|
||||
let window_height_offset = get_offset_value("engine2.dll", "dwWindowHeight")
|
||||
.expect("Failed to find dwLocalPlayerPawn offset");
|
||||
|
||||
let window_width =
|
||||
process.read_memory::<u32>(engine_base + window_width_offset as usize)?;
|
||||
|
||||
let window_height =
|
||||
process.read_memory::<u32>(engine_base + window_height_offset as usize)?;
|
||||
|
||||
println!("Window size: {}x{}", window_width, window_height);
|
||||
|
||||
|
@@ -28,7 +28,7 @@ mod util;
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "cs2-dumper")]
|
||||
#[command(author = "a2x")]
|
||||
#[command(version = "1.1.4")]
|
||||
#[command(version = "1.1.5")]
|
||||
struct Args {
|
||||
/// Dump interfaces.
|
||||
#[arg(short, long)]
|
||||
@@ -43,13 +43,13 @@ struct Args {
|
||||
schemas: bool,
|
||||
|
||||
/// List of file builders to use.
|
||||
/// Valid values are: `.cs`, `.hpp`, `.json`, `.py`, `.rs`.
|
||||
/// Valid values are: `.cs`, `.hpp`, `.json`, `.py`, `.rs`, `.yaml`.
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
value_parser = parse_extension,
|
||||
value_delimiter = ',',
|
||||
default_values = [".cs", ".hpp", ".json", ".py", ".rs"],
|
||||
default_values = [".cs", ".hpp", ".json", ".py", ".rs", ".yaml"],
|
||||
)]
|
||||
builders: Vec<FileBuilderEnum>,
|
||||
|
||||
@@ -136,6 +136,7 @@ fn parse_extension(extension: &str) -> Result<FileBuilderEnum, &'static str> {
|
||||
".json" => Ok(FileBuilderEnum::JsonFileBuilder(JsonFileBuilder::default())),
|
||||
".py" => Ok(FileBuilderEnum::PythonFileBuilder(PythonFileBuilder)),
|
||||
".rs" => Ok(FileBuilderEnum::RustFileBuilder(RustFileBuilder)),
|
||||
_ => Err("Invalid extension"),
|
||||
".yaml" => Ok(FileBuilderEnum::YamlFileBuilder(YamlFileBuilder)),
|
||||
_ => Err("Invalid file extension"),
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user