Print field types

This commit is contained in:
a2x
2023-10-01 14:43:48 +10:00
parent 18e90bcf8c
commit 6d04af2758
73 changed files with 40045 additions and 39911 deletions

View File

@@ -22,14 +22,25 @@ impl FileBuilder for CppBuilder {
Ok(())
}
fn write_variable(&mut self, output: &mut dyn Write, name: &str, value: usize) -> Result<()> {
write!(
output,
" constexpr std::ptrdiff_t {} = {:#X};\n",
name, value
)?;
Ok(())
fn write_variable(
&mut self,
output: &mut dyn Write,
name: &str,
value: usize,
comment: Option<&str>,
) -> Result<()> {
match comment {
Some(comment) => write!(
output,
" constexpr std::ptrdiff_t {} = {:#X}; // {}\n",
name, value, comment
),
None => write!(
output,
" constexpr std::ptrdiff_t {} = {:#X};\n",
name, value
),
}
}
fn write_closure(&mut self, output: &mut dyn Write, eof: bool) -> Result<()> {

View File

@@ -19,10 +19,21 @@ impl FileBuilder for CSharpBuilder {
Ok(())
}
fn write_variable(&mut self, output: &mut dyn Write, name: &str, value: usize) -> Result<()> {
write!(output, " public const nint {} = {:#X};\n", name, value)?;
Ok(())
fn write_variable(
&mut self,
output: &mut dyn Write,
name: &str,
value: usize,
comment: Option<&str>,
) -> Result<()> {
match comment {
Some(comment) => write!(
output,
" public const nint {} = {:#X}; // {}\n",
name, value, comment
),
None => write!(output, " public const nint {} = {:#X};\n", name, value),
}
}
fn write_closure(&mut self, output: &mut dyn Write, eof: bool) -> Result<()> {

View File

@@ -2,8 +2,18 @@ use std::io::{Result, Write};
pub trait FileBuilder {
fn extension(&mut self) -> &str;
fn write_top_level(&mut self, output: &mut dyn Write) -> Result<()>;
fn write_namespace(&mut self, output: &mut dyn Write, name: &str) -> Result<()>;
fn write_variable(&mut self, output: &mut dyn Write, name: &str, value: usize) -> Result<()>;
fn write_variable(
&mut self,
output: &mut dyn Write,
name: &str,
value: usize,
comment: Option<&str>,
) -> Result<()>;
fn write_closure(&mut self, output: &mut dyn Write, eof: bool) -> Result<()>;
}

View File

@@ -25,7 +25,13 @@ impl FileBuilder for JsonFileBuilder {
Ok(())
}
fn write_variable(&mut self, _output: &mut dyn Write, name: &str, value: usize) -> Result<()> {
fn write_variable(
&mut self,
_output: &mut dyn Write,
name: &str,
value: usize,
_comment: Option<&str>,
) -> Result<()> {
if let Some(json_as_map) = self.json.as_object_mut() {
let namespace_entry = json_as_map
.entry(self.namespace.clone())

View File

@@ -47,15 +47,25 @@ impl FileBuilder for FileBuilderEnum {
}
}
fn write_variable(&mut self, output: &mut dyn Write, name: &str, value: usize) -> Result<()> {
fn write_variable(
&mut self,
output: &mut dyn Write,
name: &str,
value: usize,
comment: Option<&str>,
) -> Result<()> {
match self {
FileBuilderEnum::CppBuilder(builder) => builder.write_variable(output, name, value),
FileBuilderEnum::CSharpBuilder(builder) => builder.write_variable(output, name, value),
FileBuilderEnum::CppBuilder(builder) => {
builder.write_variable(output, name, value, comment)
}
FileBuilderEnum::CSharpBuilder(builder) => {
builder.write_variable(output, name, value, comment)
}
FileBuilderEnum::JsonFileBuilder(builder) => {
builder.write_variable(output, name, value)
builder.write_variable(output, name, value, comment)
}
FileBuilderEnum::RustFileBuilder(builder) => {
builder.write_variable(output, name, value)
builder.write_variable(output, name, value, comment)
}
}
}

View File

@@ -24,10 +24,21 @@ impl FileBuilder for RustFileBuilder {
Ok(())
}
fn write_variable(&mut self, output: &mut dyn Write, name: &str, value: usize) -> Result<()> {
write!(output, " pub const {}: usize = {:#X};\n", name, value)?;
Ok(())
fn write_variable(
&mut self,
output: &mut dyn Write,
name: &str,
value: usize,
comment: Option<&str>,
) -> Result<()> {
match comment {
Some(comment) => write!(
output,
" pub const {}: usize = {:#X}; // {}\n",
name, value, comment
),
None => write!(output, " pub const {}: usize = {:#X};\n", name, value),
}
}
fn write_closure(&mut self, output: &mut dyn Write, eof: bool) -> Result<()> {

View File

@@ -1,4 +1,5 @@
use crate::builder::FileBuilderEnum;
use crate::dumpers::Entry;
use crate::error::Result;
use crate::remote::Process;
@@ -24,14 +25,14 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
while interface_registry_ptr != 0 {
let interface_ptr = process.read_memory::<usize>(interface_registry_ptr)?;
let interface_version_name_ptr =
let interface_version_ptr =
process.read_memory::<usize>(interface_registry_ptr + 0x8)?;
let interface_version_name = process.read_string(interface_version_name_ptr, 64)?;
let interface_version = process.read_string(interface_version_ptr)?;
log::info!(
" -> Found '{}' @ {:#X} ({} + {:#X})",
interface_version_name,
log::debug!(
" └─ '{}' @ {:#X} ({} + {:#X})",
interface_version,
interface_ptr,
module_name,
interface_ptr - module.address()
@@ -40,7 +41,11 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
entries
.entry(module_name.replace(".", "_"))
.or_default()
.push((interface_version_name, interface_ptr - module.address()));
.push(Entry {
name: interface_version.clone(),
value: interface_ptr - module.address(),
comment: None,
});
interface_registry_ptr =
process.read_memory::<usize>(interface_registry_ptr + 0x10)?;
@@ -48,7 +53,7 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
}
}
for builder in builders.iter_mut() {
for builder in builders {
generate_file(builder, "interfaces", &entries)?;
}

View File

@@ -12,7 +12,13 @@ pub mod interfaces;
pub mod offsets;
pub mod schemas;
pub type Entries = BTreeMap<String, Vec<(String, usize)>>;
pub struct Entry {
pub name: String,
pub value: usize,
pub comment: Option<String>,
}
pub type Entries = BTreeMap<String, Vec<Entry>>;
pub fn generate_file(
builder: &mut FileBuilderEnum,
@@ -27,11 +33,16 @@ pub fn generate_file(
let len = entries.len();
for (i, entry) in entries.iter().enumerate() {
builder.write_namespace(&mut file, entry.0)?;
for (i, pair) in entries.iter().enumerate() {
builder.write_namespace(&mut file, pair.0)?;
for (name, value) in entry.1 {
builder.write_variable(&mut file, name, *value)?;
for entry in pair.1 {
builder.write_variable(
&mut file,
&entry.name,
entry.value,
entry.comment.as_deref(),
)?;
}
builder.write_closure(&mut file, i == len - 1)?;

View File

@@ -2,6 +2,7 @@ use std::fs::File;
use crate::builder::FileBuilderEnum;
use crate::config::{Config, Operation};
use crate::dumpers::Entry;
use crate::error::{Error, Result};
use crate::remote::Process;
@@ -48,15 +49,13 @@ pub fn dump_offsets(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> R
}
}
let sanitized_module_name = signature.module.replace(".", "_");
let (name, value) = if let Some(offset) = offset {
log::info!(" -> Found '{}' @ {:#X}", signature.name, offset);
log::debug!(" └─ '{}' @ {:#X}", signature.name, offset);
(signature.name, offset as usize)
} else {
log::info!(
" -> Found '{}' @ {:#X} ({} + {:#X})",
log::debug!(
" └─ '{}' @ {:#X} ({} + {:#X})",
signature.name,
address,
signature.module,
@@ -67,12 +66,16 @@ pub fn dump_offsets(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> R
};
entries
.entry(sanitized_module_name)
.entry(signature.module.replace(".", "_"))
.or_default()
.push((name, value));
.push(Entry {
name,
value,
comment: None,
});
}
for builder in builders.iter_mut() {
for builder in builders {
generate_file(builder, "offsets", &entries)?;
}

View File

@@ -1,4 +1,5 @@
use crate::builder::FileBuilderEnum;
use crate::dumpers::Entry;
use crate::error::Result;
use crate::remote::Process;
use crate::sdk::SchemaSystem;
@@ -9,29 +10,42 @@ pub fn dump_schemas(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> R
let schema_system = SchemaSystem::new(&process)?;
for type_scope in schema_system.type_scopes()? {
log::info!("Generating files for {}...", type_scope.module_name()?);
let module_name = type_scope.module_name()?;
log::info!("Generating files for {}...", module_name);
let mut entries = Entries::new();
for class in type_scope.classes()? {
log::info!(" [{}]", class.name());
log::debug!(" {}", class.name());
for field in class.fields()? {
log::info!(" [{}] = {:#X}", field.name()?, field.offset()?);
let field_name = field.name()?;
let field_offset = field.offset()?;
let type_name = field.r#type()?.name()?;
log::debug!(
" └─ {} = {:#X} // {}",
field_name,
field_offset,
type_name
);
entries
.entry(class.name().replace("::", "_"))
.or_default()
.push((field.name()?, field.offset()? as usize));
.push(Entry {
name: field_name,
value: field_offset as usize,
comment: Some(type_name),
});
}
}
if entries.is_empty() {
continue;
}
for builder in builders.iter_mut() {
generate_file(builder, &type_scope.module_name()?, &entries)?;
if !entries.is_empty() {
for builder in builders.iter_mut() {
generate_file(builder, &module_name, &entries)?;
}
}
}

View File

@@ -1,56 +1,40 @@
use std::io;
use std::string::FromUtf8Error;
#[derive(Debug)]
use serde_json::Error as SerdeError;
use thiserror::Error;
use windows::core::Error as WindowsError;
#[derive(Debug, Error)]
pub enum Error {
#[error("Buffer size mismatch: expected {0}, got {1}")]
BufferSizeMismatch(usize, usize),
#[error("Invalid magic: {0:#X}")]
InvalidMagic(u32),
IOError(io::Error),
#[error("IO error: {0}")]
IOError(#[from] io::Error),
#[error("Module not found")]
ModuleNotFound,
#[error("Pattern not found")]
PatternNotFound,
#[error("Process not found")]
ProcessNotFound,
SectionNotFound,
SerdeError(serde_json::Error),
Utf8Error(std::string::FromUtf8Error),
WindowsError(windows::core::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::IOError(err)
}
}
#[error("Serde error: {0}")]
SerdeError(#[from] SerdeError),
impl From<std::string::FromUtf8Error> for Error {
fn from(err: std::string::FromUtf8Error) -> Self {
Self::Utf8Error(err)
}
}
#[error("UTF-8 error: {0}")]
Utf8Error(#[from] FromUtf8Error),
impl From<windows::core::Error> for Error {
fn from(err: windows::core::Error) -> Self {
Self::WindowsError(err)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::BufferSizeMismatch(expected, actual) => write!(
fmt,
"Buffer size mismatch: expected {}, got {}",
expected, actual
),
Self::InvalidMagic(magic) => write!(fmt, "Invalid magic: {:#X}", magic),
Self::IOError(err) => write!(fmt, "IO error: {}", err),
Self::ModuleNotFound => write!(fmt, "Module not found"),
Self::PatternNotFound => write!(fmt, "Pattern not found"),
Self::ProcessNotFound => write!(fmt, "Process not found"),
Self::SectionNotFound => write!(fmt, "Section not found"),
Self::SerdeError(err) => write!(fmt, "Serde error: {}", err),
Self::Utf8Error(err) => write!(fmt, "UTF-8 error: {}", err),
Self::WindowsError(err) => write!(fmt, "Windows error: {}", err),
}
}
#[error("Windows error: {0}")]
WindowsError(#[from] WindowsError),
}
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -33,24 +33,38 @@ struct Args {
#[arg(short, long)]
schemas: bool,
#[arg(short, long)]
verbose: bool,
}
fn main() -> Result<()> {
SimpleLogger::new().init().unwrap();
let Args {
all,
interfaces,
offsets,
schemas,
verbose,
} = Args::parse();
let log_level = if verbose {
log::LevelFilter::Debug
} else {
log::LevelFilter::Info
};
SimpleLogger::new()
.with_level(log_level)
.without_timestamps()
.init()
.unwrap();
let start_time = Instant::now();
fs::create_dir_all("generated")?;
let process = Process::new("cs2.exe")?;
fs::create_dir_all("generated")?;
let mut builders: Vec<FileBuilderEnum> = vec![
FileBuilderEnum::CppBuilder(CppBuilder),
FileBuilderEnum::CSharpBuilder(CSharpBuilder),

View File

@@ -11,6 +11,7 @@ use crate::error::{Error, Result};
use super::Module;
#[derive(Debug)]
pub struct Process {
process_id: u32,
process_handle: HANDLE,
@@ -157,13 +158,14 @@ impl Process {
self.write_memory_raw(address, &value as *const _ as *const _, mem::size_of::<T>())
}
pub fn read_string(&self, address: usize, length: usize) -> Result<String> {
let mut buffer: Vec<u8> = vec![0; length];
pub fn read_string(&self, address: usize) -> Result<String> {
let mut buffer = Vec::new();
self.read_memory_raw(address, buffer.as_mut_ptr() as *mut _, length)?;
if let Some(end) = buffer.iter().position(|&x| x == 0) {
buffer.truncate(end);
for i in 0.. {
match self.read_memory::<u8>(address + i) {
Ok(byte) if byte != 0 => buffer.push(byte),
_ => break,
}
}
Ok(String::from_utf8(buffer)?)

View File

@@ -2,6 +2,7 @@ pub use schema_class_field_data::SchemaClassFieldData;
pub use schema_class_info::SchemaClassInfo;
pub use schema_system::SchemaSystem;
pub use schema_system_type_scope::SchemaSystemTypeScope;
pub use schema_type::SchemaType;
pub use schema_type_declared_class::SchemaTypeDeclaredClass;
pub use utl_ts_hash::UtlTsHash;
@@ -9,5 +10,6 @@ pub mod schema_class_field_data;
pub mod schema_class_info;
pub mod schema_system;
pub mod schema_system_type_scope;
pub mod schema_type;
pub mod schema_type_declared_class;
pub mod utl_ts_hash;

View File

@@ -1,6 +1,8 @@
use crate::error::Result;
use crate::remote::Process;
use super::SchemaType;
pub struct SchemaClassFieldData<'a> {
process: &'a Process,
address: usize,
@@ -12,9 +14,15 @@ impl<'a> SchemaClassFieldData<'a> {
}
pub fn name(&self) -> Result<String> {
let name_ptr = self.process.read_memory::<usize>(self.address)?;
self.process
.read_string(self.process.read_memory::<usize>(self.address)?)
}
self.process.read_string(name_ptr, 64)
pub fn r#type(&self) -> Result<SchemaType> {
Ok(SchemaType::new(
self.process,
self.process.read_memory::<usize>(self.address + 0x8)?,
))
}
pub fn offset(&self) -> Result<u16> {

View File

@@ -28,13 +28,13 @@ impl<'a> SchemaClassInfo<'a> {
let fields: Vec<SchemaClassFieldData> = (0..count as usize)
.filter_map(|i| {
let field = self
let address = self
.process
.read_memory::<usize>(self.address + 0x28)
.ok()?
+ (i * 0x20);
(field != 0).then(|| SchemaClassFieldData::new(self.process, field))
(address != 0).then(|| SchemaClassFieldData::new(self.process, address))
})
.collect();

View File

@@ -35,6 +35,6 @@ impl<'a> SchemaSystemTypeScope<'a> {
}
pub fn module_name(&self) -> Result<String> {
self.process.read_string(self.address + 0x8, 256)
self.process.read_string(self.address + 0x8)
}
}

18
src/sdk/schema_type.rs Normal file
View File

@@ -0,0 +1,18 @@
use crate::error::Result;
use crate::remote::Process;
pub struct SchemaType<'a> {
process: &'a Process,
address: usize,
}
impl<'a> SchemaType<'a> {
pub fn new(process: &'a Process, address: usize) -> Self {
Self { process, address }
}
pub fn name(&self) -> Result<String> {
self.process
.read_string(self.process.read_memory::<usize>(self.address + 0x8)?)
}
}

View File

@@ -12,8 +12,7 @@ impl<'a> SchemaTypeDeclaredClass<'a> {
}
pub fn name(&self) -> Result<String> {
let name_ptr = self.process.read_memory::<usize>(self.address + 0x8)?;
self.process.read_string(name_ptr, 64)
self.process
.read_string(self.process.read_memory::<usize>(self.address + 0x8)?)
}
}