Compare commits

..

3 Commits

Author SHA1 Message Date
a2x
22fd5f79e6 Update README.md 2024-04-21 00:39:34 +10:00
a2x
d0c40c50cd Reorganize schema system structures into separate files 2024-04-21 00:33:24 +10:00
a2x
13b764854e Refactor argument parsing logic 2024-04-21 00:30:00 +10:00
25 changed files with 388 additions and 391 deletions

View File

@ -8,7 +8,7 @@ repository = "https://github.com/a2x/cs2-dumper"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
clap = { version = "4.5", features = ["cargo"] } clap = { version = "4.5", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
heck = "0.5" heck = "0.5"
log = "0.4" log = "0.4"

View File

@ -13,7 +13,7 @@ toolchain must be installed.
## Usage ## Usage
1. Ensure the game process is running (Being in the main menu should suffice). 1. Ensure the game process is running (Being in the main menu should suffice).
2. Run the `cs2-dumper` executable (Note that some memflow connectors may require elevated privileges to work). 2. Run the `cs2-dumper` executable (Note that some memflow connectors may require elevated privileges).
When running the executable without providing an optional memflow connector name, it will default to using the [memflow-native](https://github.com/memflow/memflow-native) cross-platform OS layer to read the memory of the game process. If you wish to use an existing memflow connector instead, pass the `connector` and optional `connector-args` arguments to the program. When running the executable without providing an optional memflow connector name, it will default to using the [memflow-native](https://github.com/memflow/memflow-native) cross-platform OS layer to read the memory of the game process. If you wish to use an existing memflow connector instead, pass the `connector` and optional `connector-args` arguments to the program.
@ -22,10 +22,11 @@ E.g. `./cs2-dumper -c pcileech -a device=fpga -vvv`
### Available Arguments ### Available Arguments
- `-c, --connector <connector>`: The name of the memflow connector to use. - `-c, --connector <connector>`: The name of the memflow connector to use.
- `-a, --connector-args <connector-args>`: Additional arguments to pass to the connector. - `-a, --connector-args <connector-args>`: Additional arguments to pass to the memflow connector.
- `-f, --file-types <file-types>`: The types of files to generate. Default: `cs`, `hpp`, `json`, `rs`. - `-f, --file-types <file-types>`: The types of files to generate. Default: `cs`, `hpp`, `json`, `rs`.
- `-o, --output <output>`: The output directory to write the generated files to. Default: `output`.
- `-i, --indent-size <indent-size>`: The number of spaces to use per indentation level. Default: `4`. - `-i, --indent-size <indent-size>`: The number of spaces to use per indentation level. Default: `4`.
- `-o, --output <output>`: The output directory to write the generated files to. Default: `output`.
- `-p, --process-name <process-name>`: The name of the game process. Default: `cs2.exe`.
- `-v...`: Increase logging verbosity. Can be specified multiple times. - `-v...`: Increase logging verbosity. Can be specified multiple times.
- `-h, --help`: Print help. - `-h, --help`: Print help.
- `-V, --version`: Print version. - `-V, --version`: Print version.

View File

@ -51,7 +51,7 @@ fn read_buttons(
((cur_button.address() - module.base) + offset_of!(KeyButton.state) as i64) as u32; ((cur_button.address() - module.base) + offset_of!(KeyButton.state) as i64) as u32;
debug!( debug!(
"found button: {} at {:#X} ({} + {:#X})", "found button: {} @ {:#X} ({} + {:#X})",
name, name,
value as u64 + module.base.to_umem(), value as u64 + module.base.to_umem(),
module.name, module.name,
@ -63,7 +63,6 @@ fn read_buttons(
cur_button = button.next; cur_button = button.next;
} }
// Sort buttons by name.
buttons.sort_unstable_by(|a, b| a.name.cmp(&b.name)); buttons.sort_unstable_by(|a, b| a.name.cmp(&b.name));
Ok(buttons) Ok(buttons)

View File

@ -58,10 +58,11 @@ fn read_interfaces(
while !cur_reg.is_null() { while !cur_reg.is_null() {
let reg = cur_reg.read(process)?; let reg = cur_reg.read(process)?;
let name = reg.name.read_string(process)?.to_string(); let name = reg.name.read_string(process)?.to_string();
let value = (reg.create_fn.address() - module.base) as u32; let value = (reg.create_fn.address() - module.base) as u32;
debug!( debug!(
"found interface: {} at {:#X} ({} + {:#X})", "found interface: {} @ {:#X} ({} + {:#X})",
name, name,
value as u64 + module.base.to_umem(), value as u64 + module.base.to_umem(),
module.name, module.name,
@ -73,7 +74,6 @@ fn read_interfaces(
cur_reg = reg.next; cur_reg = reg.next;
} }
// Sort interfaces by name.
ifaces.sort_unstable_by(|a, b| a.name.cmp(&b.name)); ifaces.sort_unstable_by(|a, b| a.name.cmp(&b.name));
Ok(ifaces) Ok(ifaces)

View File

@ -12,7 +12,6 @@ mod interfaces;
mod offsets; mod offsets;
mod schemas; mod schemas;
#[derive(Debug)]
pub struct AnalysisResult { pub struct AnalysisResult {
pub buttons: Vec<Button>, pub buttons: Vec<Button>,
pub interfaces: InterfaceMap, pub interfaces: InterfaceMap,

View File

@ -53,7 +53,7 @@ macro_rules! pattern_map {
for (name, value) in &map { for (name, value) in &map {
debug!( debug!(
"found offset: {} at {:#X} ({}.dll + {:#X})", "found offset: {} @ {:#X} ({}.dll + {:#X})",
name, name,
*value as u64 + view.optional_header().ImageBase, *value as u64 + view.optional_header().ImageBase,
stringify!($module), stringify!($module),

View File

@ -42,7 +42,7 @@ pub struct ClassField {
pub struct Enum { pub struct Enum {
pub name: String, pub name: String,
pub alignment: u8, pub alignment: u8,
pub size: i16, pub size: u16,
pub members: Vec<EnumMember>, pub members: Vec<EnumMember>,
} }
@ -114,7 +114,7 @@ fn read_class_binding(
let metadata = read_class_binding_metadata(process, &binding)?; let metadata = read_class_binding_metadata(process, &binding)?;
debug!( debug!(
"found class: {} at {:#X} (module name: {}) (parent name: {:?}) (metadata count: {}) (fields count: {})", "found class: {} @ {:#X} (module name: {}) (parent name: {:?}) (metadata count: {}) (fields count: {})",
name, name,
binding_ptr.to_umem(), binding_ptr.to_umem(),
module_name, module_name,
@ -140,7 +140,7 @@ fn read_class_binding_fields(
return Ok(Vec::new()); return Ok(Vec::new());
} }
(0..binding.num_fields).try_fold(Vec::new(), |mut acc, i| { (0..binding.field_count).try_fold(Vec::new(), |mut acc, i| {
let field = binding.fields.at(i as _).read(process)?; let field = binding.fields.at(i as _).read(process)?;
if field.schema_type.is_null() { if field.schema_type.is_null() {
@ -156,7 +156,7 @@ fn read_class_binding_fields(
acc.push(ClassField { acc.push(ClassField {
name, name,
type_name, type_name,
offset: field.offset, offset: field.single_inheritance_offset,
}); });
Ok(acc) Ok(acc)
@ -171,24 +171,28 @@ fn read_class_binding_metadata(
return Ok(Vec::new()); return Ok(Vec::new());
} }
(0..binding.num_static_metadata).try_fold(Vec::new(), |mut acc, i| { (0..binding.static_metadata_count).try_fold(Vec::new(), |mut acc, i| {
let metadata = binding.static_metadata.at(i as _).read(process)?; let metadata = binding.static_metadata.at(i as _).read(process)?;
if metadata.network_value.is_null() { if metadata.data.is_null() {
return Ok(acc); return Ok(acc);
} }
let name = metadata.name.read_string(process)?.to_string(); let name = metadata.name.read_string(process)?.to_string();
let network_value = metadata.network_value.read(process)?; let network_value = metadata.data.read(process)?;
let metadata = match name.as_str() { let metadata = match name.as_str() {
"MNetworkChangeCallback" => unsafe { "MNetworkChangeCallback" => unsafe {
let name = network_value.u.name_ptr.read_string(process)?.to_string(); let name = network_value
.value
.name_ptr
.read_string(process)?
.to_string();
ClassMetadata::NetworkChangeCallback { name } ClassMetadata::NetworkChangeCallback { name }
}, },
"MNetworkVarNames" => unsafe { "MNetworkVarNames" => unsafe {
let var_value = network_value.u.var_value; let var_value = network_value.value.var_value;
let name = var_value.name.read_string(process)?.to_string(); let name = var_value.name.read_string(process)?.to_string();
let type_name = var_value.type_name.read_string(process)?.replace(" ", ""); let type_name = var_value.type_name.read_string(process)?.replace(" ", "");
@ -214,7 +218,7 @@ fn read_enum_binding(
let members = read_enum_binding_members(process, &binding)?; let members = read_enum_binding_members(process, &binding)?;
debug!( debug!(
"found enum: {} at {:#X} (alignment: {}) (members count: {})", "found enum: {} @ {:#X} (alignment: {}) (members count: {})",
name, name,
binding_ptr.to_umem(), binding_ptr.to_umem(),
binding.alignment, binding.alignment,
@ -224,7 +228,7 @@ fn read_enum_binding(
Ok(Enum { Ok(Enum {
name, name,
alignment: binding.alignment, alignment: binding.alignment,
size: binding.num_enumerators, size: binding.enumerator_count,
members, members,
}) })
} }
@ -237,13 +241,13 @@ fn read_enum_binding_members(
return Ok(Vec::new()); return Ok(Vec::new());
} }
(0..binding.num_enumerators).try_fold(Vec::new(), |mut acc, i| { (0..binding.enumerator_count).try_fold(Vec::new(), |mut acc, i| {
let enumerator = binding.enumerators.at(i as _).read(process)?; let enumerator = binding.enumerators.at(i as _).read(process)?;
let name = enumerator.name.read_string(process)?.to_string(); let name = enumerator.name.read_string(process)?.to_string();
acc.push(EnumMember { acc.push(EnumMember {
name, name,
value: unsafe { enumerator.u.ulong } as i64, value: unsafe { enumerator.value.ulong } as i64,
}); });
Ok(acc) Ok(acc)
@ -307,7 +311,7 @@ fn read_type_scopes(
} }
debug!( debug!(
"found type scope: {} at {:#X} (classes count: {}) (enums count: {})", "found type scope: {} @ {:#X} (classes count: {}) (enums count: {})",
module_name, module_name,
type_scope_ptr.to_umem(), type_scope_ptr.to_umem(),
classes.len(), classes.len(),

View File

@ -1,4 +1,5 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr;
use std::time::Instant; use std::time::Instant;
use clap::*; use clap::*;
@ -18,107 +19,47 @@ mod mem;
mod output; mod output;
mod source2; mod source2;
const PROCESS_NAME: &str = "cs2.exe"; #[derive(Debug, Parser)]
#[command(author, version)]
struct Args {
/// The name of the memflow connector to use.
#[arg(short, long)]
connector: Option<String>,
/// Additional arguments to pass to the memflow connector.
#[arg(short = 'a', long)]
connector_args: Option<String>,
/// The types of files to generate.
#[arg(short, long, value_delimiter = ',', default_values = ["cs", "hpp", "json", "rs"])]
file_types: Vec<String>,
/// The number of spaces to use per indentation level.
#[arg(short, long, default_value_t = 4)]
indent_size: usize,
/// The output directory to write the generated files to.
#[arg(short, long, default_value = "output")]
output: PathBuf,
/// The name of the game process.
#[arg(short, long, default_value = "cs2.exe")]
process_name: String,
/// Increase logging verbosity. Can be specified multiple times.
#[arg(short, action = ArgAction::Count)]
verbose: u8,
}
fn main() -> Result<()> { fn main() -> Result<()> {
let args = Args::parse();
let now = Instant::now(); let now = Instant::now();
let matches = parse_args(); let log_level = match args.verbose {
let (conn_name, conn_args, file_types, indent_size, out_dir) = extract_args(&matches)?;
let os = if let Some(conn_name) = conn_name {
let inventory = Inventory::scan();
inventory
.builder()
.connector(&conn_name)
.args(conn_args)
.os("win32")
.build()?
} else {
// Fallback to the native OS layer if no connector name was specified.
memflow_native::create_os(&Default::default(), Default::default())?
};
let mut process = os.into_process_by_name(PROCESS_NAME)?;
let result = analysis::analyze_all(&mut process)?;
let output = Output::new(&file_types, indent_size, &out_dir, &result)?;
output.dump_all(&mut process)?;
info!("finished in {:?}", now.elapsed());
Ok(())
}
fn parse_args() -> ArgMatches {
Command::new("cs2-dumper")
.version(crate_version!())
.author(crate_authors!())
.arg(
Arg::new("connector")
.help("The name of the memflow connector to use.")
.long("connector")
.short('c')
.required(false),
)
.arg(
Arg::new("connector-args")
.help("Additional arguments to pass to the connector.")
.long("connector-args")
.short('a')
.required(false),
)
.arg(
Arg::new("file-types")
.help("The types of files to generate.")
.long("file-types")
.short('f')
.action(ArgAction::Append)
.default_values(["cs", "hpp", "json", "rs"])
.value_parser(["cs", "hpp", "json", "rs"])
.value_delimiter(',')
.required(false),
)
.arg(
Arg::new("indent-size")
.help("The number of spaces to use per indentation level.")
.long("indent-size")
.short('i')
.default_value("4")
.value_parser(value_parser!(usize))
.required(false),
)
.arg(
Arg::new("output")
.help("The output directory to write the generated files to.")
.long("output")
.short('o')
.default_value("output")
.value_parser(value_parser!(PathBuf))
.required(false),
)
.arg(
Arg::new("verbose")
.help("Increase logging verbosity. Can be specified multiple times.")
.short('v')
.action(ArgAction::Count),
)
.get_matches()
}
fn extract_args(
matches: &ArgMatches,
) -> Result<(Option<String>, ConnectorArgs, Vec<String>, usize, &PathBuf)> {
use std::str::FromStr;
let log_level = match matches.get_count("verbose") {
0 => Level::Error, 0 => Level::Error,
1 => Level::Warn, 1 => Level::Warn,
2 => Level::Info, 2 => Level::Info,
3 => Level::Debug, 3 => Level::Debug,
4 => Level::Trace,
_ => Level::Trace, _ => Level::Trace,
}; };
@ -130,23 +71,33 @@ fn extract_args(
) )
.unwrap(); .unwrap();
let conn_name = matches let conn_args = args
.get_one::<String>("connector") .connector_args
.map(|s| s.to_string());
let conn_args = matches
.get_one::<String>("connector-args")
.map(|s| ConnectorArgs::from_str(&s).expect("unable to parse connector arguments")) .map(|s| ConnectorArgs::from_str(&s).expect("unable to parse connector arguments"))
.unwrap_or_default(); .unwrap_or_default();
let file_types = matches let os = if let Some(conn) = args.connector {
.get_many::<String>("file-types") let inventory = Inventory::scan();
.unwrap()
.map(|s| s.to_string())
.collect();
let indent_size = *matches.get_one::<usize>("indent-size").unwrap(); inventory
let out_dir = matches.get_one::<PathBuf>("output").unwrap(); .builder()
.connector(&conn)
.args(conn_args)
.os("win32")
.build()?
} else {
// Fallback to the native OS layer if no connector name was specified.
memflow_native::create_os(&Default::default(), Default::default())?
};
Ok((conn_name, conn_args, file_types, indent_size, out_dir)) let mut process = os.into_process_by_name(&args.process_name)?;
let result = analysis::analyze_all(&mut process)?;
let output = Output::new(&args.file_types, args.indent_size, &args.output, &result)?;
output.dump_all(&mut process)?;
info!("finished in {:?}", now.elapsed());
Ok(())
} }

View File

@ -1,7 +1,6 @@
use memflow::types::{Pointer, PrimitiveAddress}; use memflow::types::{Pointer, PrimitiveAddress};
pub trait PointerExt { pub trait PointerExt {
/// Returns `true` if the pointer is null.
fn is_null(&self) -> bool; fn is_null(&self) -> bool;
} }

View File

@ -6,7 +6,6 @@ use chrono::{DateTime, Utc};
use memflow::prelude::v1::*; use memflow::prelude::v1::*;
use serde::Serialize;
use serde_json::json; use serde_json::json;
use formatter::Formatter; use formatter::Formatter;
@ -20,8 +19,6 @@ mod interfaces;
mod offsets; mod offsets;
mod schemas; mod schemas;
#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
enum Item<'a> { enum Item<'a> {
Buttons(&'a Vec<Button>), Buttons(&'a Vec<Button>),
Interfaces(&'a InterfaceMap), Interfaces(&'a InterfaceMap),
@ -30,8 +27,8 @@ enum Item<'a> {
} }
impl<'a> Item<'a> { impl<'a> Item<'a> {
fn write(&self, fmt: &mut Formatter<'a>, file_ext: &str) -> fmt::Result { fn write(&self, fmt: &mut Formatter<'a>, file_type: &str) -> fmt::Result {
match file_ext { match file_type {
"cs" => self.write_cs(fmt), "cs" => self.write_cs(fmt),
"hpp" => self.write_hpp(fmt), "hpp" => self.write_hpp(fmt),
"json" => self.write_json(fmt), "json" => self.write_json(fmt),

View File

@ -1,244 +1,21 @@
use std::ffi::c_char; pub use schema_base_class_info_data::*;
pub use schema_class_field_data::*;
pub use schema_class_info_data::*;
pub use schema_enum_info_data::*;
pub use schema_enumerator_info_data::*;
pub use schema_metadata_entry_data::*;
pub use schema_static_field_data::*;
pub use schema_system::*;
pub use schema_system_type_scope::*;
pub use schema_type::*;
use memflow::prelude::v1::*; pub mod schema_base_class_info_data;
pub mod schema_class_field_data;
use super::{UtlTsHash, UtlVector}; pub mod schema_class_info_data;
pub mod schema_enum_info_data;
pub type SchemaClassBinding = SchemaClassInfoData; pub mod schema_enumerator_info_data;
pub type SchemaEnumBinding = SchemaEnumInfoData; pub mod schema_metadata_entry_data;
pub type SchemaTypeDeclaredClass = SchemaType; pub mod schema_static_field_data;
pub type SchemaTypeDeclaredEnum = SchemaType; pub mod schema_system;
pub mod schema_system_type_scope;
#[repr(u8)] pub mod schema_type;
pub enum SchemaAtomicCategory {
Basic = 0,
T,
CollectionOfT,
TF,
TT,
TTF,
I,
None,
}
#[repr(u8)]
pub enum SchemaTypeCategory {
BuiltIn = 0,
Ptr,
Bitfield,
FixedArray,
Atomic,
DeclaredClass,
DeclaredEnum,
None,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaArrayT {
pub array_size: u32, // 0x0000
pad_0004: [u8; 0x4], // 0x0004
pub element: Pointer64<SchemaType>, // 0x0008
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicI {
pad_0000: [u8; 0x10], // 0x0000
pub value: u64, // 0x0010
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicT {
pub element: Pointer64<SchemaType>, // 0x0000
pad_0008: [u8; 0x8], // 0x0008
pub template: Pointer64<SchemaType>, // 0x0010
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicTT {
pad_0000: [u8; 0x10], // 0x0000
pub templates: [Pointer64<SchemaType>; 2], // 0x0010
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicTF {
pad_0000: [u8; 0x10], // 0x0000
pub template: Pointer64<SchemaType>, // 0x0010
pub size: i32, // 0x0018
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicTTF {
pad_0000: [u8; 0x10], // 0x0000
pub templates: [Pointer64<SchemaType>; 2], // 0x0010
pub size: i32, // 0x0020
}
#[derive(Pod)]
#[repr(C)]
pub struct SchemaBaseClassInfoData {
pub offset: i32, // 0x0000
pad_0004: [u8; 0x4], // 0x0004
pub prev: Pointer64<SchemaClassInfoData>, // 0x0008
}
#[derive(Pod)]
#[repr(C)]
pub struct SchemaClassFieldData {
pub name: Pointer64<ReprCString>, // 0x0000
pub schema_type: Pointer64<SchemaType>, // 0x0008
pub offset: i32, // 0x0010
pub num_metadata: i32, // 0x0014
pub metadata: Pointer64<SchemaMetadataEntryData>, // 0x0018
}
#[rustfmt::skip]
#[derive(Pod)]
#[repr(C)]
pub struct SchemaClassInfoData {
pub base: Pointer64<SchemaClassInfoData>, // 0x0000
pub name: Pointer64<ReprCString>, // 0x0008
pub module_name: Pointer64<ReprCString>, // 0x0010
pub size: i32, // 0x0018
pub num_fields: i16, // 0x001C
pub num_static_fields: i16, // 0x001E
pub num_static_metadata: i16, // 0x0020
pub alignment: u8, // 0x0022
pub has_base_class: u8, // 0x0023
pub total_class_size: i16, // 0x0024
pub derived_class_size: i16, // 0x0026
pub fields: Pointer64<[SchemaClassFieldData]>, // 0x0028
pub static_fields: Pointer64<[SchemaStaticFieldData]>, // 0x0030
pub base_classes: Pointer64<SchemaBaseClassInfoData>, // 0x0038
pad_0040: [u8; 0x8], // 0x0040
pub static_metadata: Pointer64<[SchemaMetadataEntryData]>, // 0x0048
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0050
pub schema_type: Pointer64<SchemaType>, // 0x0058
pad_0060: [u8; 0x10], // 0x0060
}
#[rustfmt::skip]
#[derive(Pod)]
#[repr(C)]
pub struct SchemaEnumInfoData {
pub base: Pointer64<SchemaEnumInfoData>, // 0x0000
pub name: Pointer64<ReprCString>, // 0x0008
pub module_name: Pointer64<ReprCString>, // 0x0010
pub size: u8, // 0x0018
pub alignment: u8, // 0x0019
pad_001a: u16, // 0x001A
pub num_enumerators: i16, // 0x001C
pub num_static_metadata: i16, // 0x001E
pub enumerators: Pointer64<[SchemaEnumeratorInfoData]>, // 0x0020
pub static_metadata: Pointer64<SchemaMetadataEntryData>, // 0x0028
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0030
pad_0038: [u8; 0x10], // 0x0038
}
#[repr(C)]
pub struct SchemaEnumeratorInfoData {
pub name: Pointer64<ReprCString>, // 0x0000
pub u: SchemaEnumeratorInfoDataUnion, // 0x0008
pub num_metadata: u32, // 0x0010
pub metadata: Pointer64<SchemaMetadataEntryData>, // 0x0018
}
unsafe impl Pod for SchemaEnumeratorInfoData {}
#[repr(C)]
pub union SchemaEnumeratorInfoDataUnion {
pub uchar: u8,
pub ushort: u16,
pub uint: u32,
pub ulong: u64,
}
#[derive(Pod)]
#[repr(C)]
pub struct SchemaMetadataEntryData {
pub name: Pointer64<ReprCString>, // 0x0000
pub network_value: Pointer64<SchemaNetworkValue>, // 0x0008
}
#[repr(C)]
pub struct SchemaNetworkValue {
pub u: SchemaNetworkValueUnion, // 0x0000
}
unsafe impl Pod for SchemaNetworkValue {}
#[repr(C)]
pub union SchemaNetworkValueUnion {
pub name_ptr: Pointer64<ReprCString>,
pub int_value: i32,
pub float_value: f32,
pub ptr: Pointer64<()>,
pub var_value: SchemaVarName,
pub name_value: [c_char; 32],
}
#[derive(Pod)]
#[repr(C)]
pub struct SchemaStaticFieldData {
pub name: Pointer64<ReprCString>, // 0x0000
pub type_: Pointer64<SchemaType>, // 0x0008
pub instance: Pointer64<()>, // 0x0010
pad_0018: [u8; 0x10], // 0x0018
}
#[derive(Pod)]
#[repr(C)]
pub struct SchemaSystem {
pad_0000: [u8; 0x190], // 0x0000
pub type_scopes: UtlVector<Pointer64<SchemaSystemTypeScope>>, // 0x0190
pad_01a0: [u8; 0x120], // 0x01A0
pub num_registrations: u32, // 0x02C0
pad_02c4: [u8; 0x4], // 0x02C4
}
#[derive(Pod)]
#[repr(C)]
pub struct SchemaSystemTypeScope {
pad_0000: [u8; 0x8], // 0x0000
pub name: [c_char; 256], // 0x0008
pub global_scope: Pointer64<SchemaSystemTypeScope>, // 0x0108
pad_0110: [u8; 0x4B0], // 0x0110
pub class_bindings: UtlTsHash<Pointer64<SchemaClassBinding>>, // 0x05C0
pub enum_bindings: UtlTsHash<Pointer64<SchemaEnumBinding>>, // 0x2E50
}
#[repr(C)]
pub struct SchemaType {
pad_0000: [u8; 0x8], // 0x0000
pub name: Pointer64<ReprCString>, // 0x0008
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0010
pub type_category: SchemaTypeCategory, // 0x0018
pub atomic_category: SchemaAtomicCategory, // 0x0019
pub u: SchemaTypeUnion, // 0x0020
}
unsafe impl Pod for SchemaType {}
pub union SchemaTypeUnion {
pub schema_type: Pointer64<SchemaType>,
pub class_binding: Pointer64<SchemaClassBinding>,
pub enum_binding: Pointer64<SchemaEnumBinding>,
pub array: SchemaArrayT,
pub atomic: SchemaAtomicT,
pub atomic_tt: SchemaAtomicTT,
pub atomic_tf: SchemaAtomicTF,
pub atomic_ttf: SchemaAtomicTTF,
pub atomic_i: SchemaAtomicI,
}
#[derive(Pod, Clone, Copy)]
#[repr(C)]
pub struct SchemaVarName {
pub name: Pointer64<ReprCString>, // 0x0000
pub type_name: Pointer64<ReprCString>, // 0x0008
}

View File

@ -0,0 +1,11 @@
use memflow::prelude::v1::*;
use super::SchemaClassInfoData;
#[derive(Pod)]
#[repr(C)]
pub struct SchemaBaseClassInfoData {
pub offset: u32, // 0x0000
pad_0004: [u8; 4], // 0x0004
pub prev: Pointer64<SchemaClassInfoData>, // 0x0008
}

View File

@ -0,0 +1,13 @@
use memflow::prelude::v1::*;
use super::{SchemaMetadataEntryData, SchemaType};
#[derive(Pod)]
#[repr(C)]
pub struct SchemaClassFieldData {
pub name: Pointer64<ReprCString>, // 0x0000
pub schema_type: Pointer64<SchemaType>, // 0x0008
pub single_inheritance_offset: i32, // 0x0010
pub static_metadata_count: i32, // 0x0014
pub static_metadata: Pointer64<SchemaMetadataEntryData>, // 0x0018
}

View File

@ -0,0 +1,30 @@
use memflow::prelude::v1::*;
use super::*;
pub type SchemaClassBinding = SchemaClassInfoData;
#[rustfmt::skip]
#[derive(Pod)]
#[repr(C)]
pub struct SchemaClassInfoData {
pub base: Pointer64<SchemaClassInfoData>, // 0x0000
pub name: Pointer64<ReprCString>, // 0x0008
pub module_name: Pointer64<ReprCString>, // 0x0010
pub size: i32, // 0x0018
pub field_count: u16, // 0x001C
pub static_field_count: u16, // 0x001E
pub static_metadata_count: u16, // 0x0020
pub alignment: u8, // 0x0022
pub base_class_count: u8, // 0x0023
pub multiple_inheritance_depth: u16, // 0x0024
pub single_inheritance_depth: u16, // 0x0026
pub fields: Pointer64<[SchemaClassFieldData]>, // 0x0028
pub static_fields: Pointer64<[SchemaStaticFieldData]>, // 0x0030
pub base_classes: Pointer64<SchemaBaseClassInfoData>, // 0x0038
pad_0040: [u8; 0x8], // 0x0040
pub static_metadata: Pointer64<[SchemaMetadataEntryData]>, // 0x0048
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0050
pub schema_type: Pointer64<SchemaType>, // 0x0058
pad_0060: [u8; 0x10], // 0x0060
}

View File

@ -0,0 +1,24 @@
use memflow::prelude::v1::*;
use super::{SchemaEnumeratorInfoData, SchemaMetadataEntryData, SchemaSystemTypeScope};
pub type SchemaEnumBinding = SchemaEnumInfoData;
#[rustfmt::skip]
#[derive(Pod)]
#[repr(C)]
pub struct SchemaEnumInfoData {
pub base: Pointer64<SchemaEnumInfoData>, // 0x0000
pub name: Pointer64<ReprCString>, // 0x0008
pub module_name: Pointer64<ReprCString>, // 0x0010
pub size: u8, // 0x0018
pub alignment: u8, // 0x0019
pub flags: u16, // 0x001A
pub enumerator_count: u16, // 0x001C
pub static_metadata_count: u16, // 0x001E
pub enumerators: Pointer64<[SchemaEnumeratorInfoData]>, // 0x0020
pub static_metadata: Pointer64<SchemaMetadataEntryData>, // 0x0028
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0030
pub min_enumerator_value: i64, // 0x0038
pub max_enumerator_value: i64, // 0x0040
}

View File

@ -0,0 +1,23 @@
use memflow::prelude::v1::*;
use super::SchemaMetadataEntryData;
#[derive(Pod)]
#[repr(C)]
pub struct SchemaEnumeratorInfoData {
pub name: Pointer64<ReprCString>, // 0x0000
pub value: SchemaEnumeratorInfoDataUnion, // 0x0008
pub static_metadata_count: i32, // 0x0010
pad_0014: [u8; 0x4], // 0x0014
pub static_metadata: Pointer64<SchemaMetadataEntryData>, // 0x0018
}
#[repr(C)]
pub union SchemaEnumeratorInfoDataUnion {
pub uchar: u8,
pub ushort: u16,
pub uint: u32,
pub ulong: u64,
}
unsafe impl Pod for SchemaEnumeratorInfoDataUnion {}

View File

@ -0,0 +1,35 @@
use std::ffi::c_char;
use memflow::prelude::v1::*;
#[derive(Pod)]
#[repr(C)]
pub struct SchemaMetadataEntryData {
pub name: Pointer64<ReprCString>, // 0x0000
pub data: Pointer64<SchemaNetworkValue>, // 0x0008
}
#[derive(Pod)]
#[repr(C)]
pub struct SchemaNetworkValue {
pub value: SchemaNetworkValueUnion, // 0x0000
}
#[repr(C)]
pub union SchemaNetworkValueUnion {
pub name_ptr: Pointer64<ReprCString>,
pub int_value: i32,
pub float_value: f32,
pub ptr: Pointer64<()>,
pub var_value: SchemaVarName,
pub name_value: [c_char; 32],
}
unsafe impl Pod for SchemaNetworkValueUnion {}
#[derive(Clone, Copy, Pod)]
#[repr(C)]
pub struct SchemaVarName {
pub name: Pointer64<ReprCString>, // 0x0000
pub type_name: Pointer64<ReprCString>, // 0x0008
}

View File

@ -0,0 +1,14 @@
use memflow::prelude::v1::*;
use super::{SchemaMetadataEntryData, SchemaType};
#[derive(Pod)]
#[repr(C)]
pub struct SchemaStaticFieldData {
pub name: Pointer64<ReprCString>, // 0x0000
pub type_: Pointer64<SchemaType>, // 0x0008
pub instance: Pointer64<()>, // 0x0010
pub static_metadata_count: i32, // 0x0018
pad_001c: [u8; 0x4], // 0x001C
pub static_metadata: Pointer64<SchemaMetadataEntryData>, // 0x0020
}

View File

@ -0,0 +1,15 @@
use memflow::prelude::v1::*;
use super::SchemaSystemTypeScope;
use crate::source2::UtlVector;
#[derive(Pod)]
#[repr(C)]
pub struct SchemaSystem {
pad_0000: [u8; 0x190], // 0x0000
pub type_scopes: UtlVector<Pointer64<SchemaSystemTypeScope>>, // 0x0190
pad_01a0: [u8; 0x120], // 0x01A0
pub num_registrations: u32, // 0x02C0
pad_02c4: [u8; 0xAC], // 0x02C4
}

View File

@ -0,0 +1,18 @@
use std::ffi::c_char;
use memflow::prelude::v1::*;
use super::{SchemaClassBinding, SchemaEnumBinding};
use crate::source2::UtlTsHash;
#[derive(Pod)]
#[repr(C)]
pub struct SchemaSystemTypeScope {
pad_0000: [u8; 0x8], // 0x0000
pub name: [c_char; 256], // 0x0008
pub global_scope: Pointer64<SchemaSystemTypeScope>, // 0x0108
pad_0110: [u8; 0x4B0], // 0x0110
pub class_bindings: UtlTsHash<Pointer64<SchemaClassBinding>>, // 0x05C0
pub enum_bindings: UtlTsHash<Pointer64<SchemaEnumBinding>>, // 0x2E50
}

View File

@ -0,0 +1,97 @@
use memflow::prelude::v1::*;
use super::{SchemaClassBinding, SchemaEnumBinding, SchemaSystemTypeScope};
#[repr(u8)]
pub enum SchemaAtomicCategory {
Basic = 0,
T,
CollectionOfT,
TF,
TT,
TTF,
I,
None,
}
#[repr(u8)]
pub enum SchemaTypeCategory {
BuiltIn = 0,
Ptr,
Bitfield,
FixedArray,
Atomic,
DeclaredClass,
DeclaredEnum,
None,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaArrayT {
pub array_size: u32, // 0x0000
pad_0004: [u8; 0x4], // 0x0004
pub element: Pointer64<SchemaType>, // 0x0008
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicI {
pad_0000: [u8; 0x10], // 0x0000
pub value: u64, // 0x0010
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicT {
pub element: Pointer64<SchemaType>, // 0x0000
pad_0008: [u8; 0x8], // 0x0008
pub template: Pointer64<SchemaType>, // 0x0010
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicTT {
pad_0000: [u8; 0x10], // 0x0000
pub templates: [Pointer64<SchemaType>; 2], // 0x0010
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicTF {
pad_0000: [u8; 0x10], // 0x0000
pub template: Pointer64<SchemaType>, // 0x0010
pub size: i32, // 0x0018
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct SchemaAtomicTTF {
pad_0000: [u8; 0x10], // 0x0000
pub templates: [Pointer64<SchemaType>; 2], // 0x0010
pub size: i32, // 0x0020
}
#[repr(C)]
pub struct SchemaType {
pad_0000: [u8; 0x8], // 0x0000
pub name: Pointer64<ReprCString>, // 0x0008
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0010
pub type_category: SchemaTypeCategory, // 0x0018
pub atomic_category: SchemaAtomicCategory, // 0x0019
pub value: SchemaTypeUnion, // 0x0020
}
unsafe impl Pod for SchemaType {}
pub union SchemaTypeUnion {
pub schema_type: Pointer64<SchemaType>,
pub class_binding: Pointer64<SchemaClassBinding>,
pub enum_binding: Pointer64<SchemaEnumBinding>,
pub array: SchemaArrayT,
pub atomic: SchemaAtomicT,
pub atomic_tt: SchemaAtomicTT,
pub atomic_tf: SchemaAtomicTF,
pub atomic_ttf: SchemaAtomicTTF,
pub atomic_i: SchemaAtomicI,
}

View File

@ -10,13 +10,11 @@ pub struct UtlMemory<T> {
} }
impl<T: Pod> UtlMemory<T> { impl<T: Pod> UtlMemory<T> {
/// Returns the number of allocated elements.
#[inline] #[inline]
pub fn count(&self) -> i32 { pub fn count(&self) -> i32 {
self.alloc_count self.alloc_count
} }
/// Returns the element at the specified index.
pub fn element(&self, process: &mut IntoProcessInstanceArcBox<'_>, idx: usize) -> Result<T> { pub fn element(&self, process: &mut IntoProcessInstanceArcBox<'_>, idx: usize) -> Result<T> {
if idx >= self.count() as usize { if idx >= self.count() as usize {
return Err(Error::Other("index out of bounds")); return Err(Error::Other("index out of bounds"));
@ -25,7 +23,6 @@ impl<T: Pod> UtlMemory<T> {
self.mem.at(idx as _).read(process).map_err(Into::into) self.mem.at(idx as _).read(process).map_err(Into::into)
} }
/// Returns `true` if the memory was externally allocated.
#[inline] #[inline]
pub fn is_externally_allocated(&self) -> bool { pub fn is_externally_allocated(&self) -> bool {
self.grow_size < 0 self.grow_size < 0

View File

@ -30,7 +30,7 @@ pub struct UtlMemoryPoolBase {
pub blocks_alloc: i32, // 0x000C pub blocks_alloc: i32, // 0x000C
pub peak_alloc: i32, // 0x0010 pub peak_alloc: i32, // 0x0010
pub alignment: u16, // 0x0014 pub alignment: u16, // 0x0014
pub num_blobs: u16, // 0x0016 pub blob_count: u16, // 0x0016
pub free_list_tail: Pointer64<Pointer64<FreeList>>, // 0x0018 pub free_list_tail: Pointer64<Pointer64<FreeList>>, // 0x0018
pub free_list_head: Pointer64<FreeList>, // 0x0020 pub free_list_head: Pointer64<FreeList>, // 0x0020
pad_0028: [u8; 0x44], // 0x0028 pad_0028: [u8; 0x44], // 0x0028
@ -40,7 +40,6 @@ pub struct UtlMemoryPoolBase {
} }
impl UtlMemoryPoolBase { impl UtlMemoryPoolBase {
/// Returns the total size of the memory pool.
#[inline] #[inline]
pub fn size(&self) -> i32 { pub fn size(&self) -> i32 {
self.total_size self.total_size

View File

@ -44,25 +44,21 @@ where
D: Pod + PointerExt, D: Pod + PointerExt,
K: Pod, K: Pod,
{ {
/// Returns the number of allocated blocks.
#[inline] #[inline]
pub fn blocks_alloc(&self) -> i32 { pub fn blocks_alloc(&self) -> i32 {
self.entry_mem.blocks_alloc self.entry_mem.blocks_alloc
} }
/// Returns the size of a block.
#[inline] #[inline]
pub fn block_size(&self) -> i32 { pub fn block_size(&self) -> i32 {
self.entry_mem.block_size self.entry_mem.block_size
} }
/// Returns the maximum number of allocated blocks.
#[inline] #[inline]
pub fn peak_count(&self) -> i32 { pub fn peak_count(&self) -> i32 {
self.entry_mem.peak_alloc self.entry_mem.peak_alloc
} }
/// Returns a list of allocated or unallocated elements.
pub fn elements(&self, process: &mut IntoProcessInstanceArcBox<'_>) -> Result<Vec<D>> { pub fn elements(&self, process: &mut IntoProcessInstanceArcBox<'_>) -> Result<Vec<D>> {
let blocks_alloc = self.blocks_alloc() as usize; let blocks_alloc = self.blocks_alloc() as usize;
let peak_alloc = self.peak_count() as usize; let peak_alloc = self.peak_count() as usize;

View File

@ -9,13 +9,11 @@ pub struct UtlVector<T> {
} }
impl<T: Pod> UtlVector<T> { impl<T: Pod> UtlVector<T> {
/// Returns the number of elements in the vector.
#[inline] #[inline]
pub fn count(&self) -> i32 { pub fn count(&self) -> i32 {
self.size self.size
} }
/// Returns the element at the specified index.
pub fn element(&self, process: &mut IntoProcessInstanceArcBox<'_>, idx: usize) -> Result<T> { pub fn element(&self, process: &mut IntoProcessInstanceArcBox<'_>, idx: usize) -> Result<T> {
if idx >= self.count() as usize { if idx >= self.count() as usize {
return Err(Error::Other("index out of bounds")); return Err(Error::Other("index out of bounds"));