Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 0 additions & 41 deletions src/game_engine/unity/il2cpp/assembly.rs

This file was deleted.

127 changes: 17 additions & 110 deletions src/game_engine/unity/il2cpp/class.rs
Original file line number Diff line number Diff line change
@@ -1,93 +1,17 @@
use core::iter::{self, FusedIterator};

use super::{super::get_backing_name, Field, Module, CSTR};
use crate::{future::retry, string::ArrayCString, Address, Error, Process};
use super::super::managed::ClassRef;
use super::Module;
use crate::{future::retry, Address, Process};

#[cfg(feature = "derive")]
pub use asr_derive::Il2cppClass as Class;

/// A .NET class that is part of an [`Image`](Image).
/// A .NET class that is part of an [`Image`](super::Image).
#[derive(Copy, Clone)]
pub struct Class {
pub(super) class: Address,
}

impl Class {
pub(super) fn get_name<const N: usize>(
&self,
process: &Process,
module: &Module,
) -> Result<ArrayCString<N>, Error> {
process
.read_pointer(self.class + module.offsets.class.name, module.pointer_size)
.and_then(|addr| process.read(addr))
}

pub(super) fn get_name_space<const N: usize>(
&self,
process: &Process,
module: &Module,
) -> Result<ArrayCString<N>, Error> {
process
.read_pointer(
self.class + module.offsets.class.namespace,
module.pointer_size,
)
.and_then(|addr| process.read(addr))
}

fn fields<'a>(
&'a self,
process: &'a Process,
module: &'a Module,
) -> impl FusedIterator<Item = Field> + 'a {
let mut this_class = Some(*self);

iter::from_fn(move || {
let class = this_class?;

if class
.get_name::<CSTR>(process, module)
.ok()?
.matches("Object")
|| class
.get_name_space::<CSTR>(process, module)
.ok()?
.matches("UnityEngine")
{
return None;
}

// Prepare for next iteration
this_class = class.get_parent(process, module);

let field_count = process
.read::<u16>(class.class + module.offsets.class.field_count)
.ok()
.filter(|&val| val != u16::MAX)
.unwrap_or_default() as u64;

let fields = match field_count {
0 => None,
_ => process
.read_pointer(
class.class + module.offsets.class.fields,
module.pointer_size,
)
.ok()
.filter(|addr| !addr.is_null()),
};

Some((0..field_count).filter_map(move |i| {
fields.map(|fields| Field {
field: fields + i.wrapping_mul(module.offsets.field.struct_size as _),
})
}))
})
.flatten()
.fuse()
}

/// Tries to find a field with the specified name in the class. This returns
/// the offset of the field from the start of an instance of the class. If
/// it's a static field, the offset will be from the start of the static
Expand All @@ -98,20 +22,10 @@ impl Class {
module: &Module,
field_name: &str,
) -> Option<u32> {
self.fields(process, module)
.find(|field| {
field.get_name::<CSTR>(process, module).is_ok_and(|name| {
// If the name matches, return immediately
name.matches(field_name)

// BackingField pattern: <FieldName>k__BackingField
|| name.validate_utf8()
.ok()
.and_then(|name| get_backing_name(name))
.is_some_and(|name| name == field_name)
})
})
.and_then(|field| field.get_offset(process, module))
module
.walk()
.find_field_offset(process, ClassRef::new(self.class), field_name)
.map(|(_, offset)| offset)
}

/// Tries to find the address of a static instance of the class based on its
Expand All @@ -137,29 +51,22 @@ impl Class {
.await
}

fn get_static_table_pointer(&self, module: &Module) -> Address {
self.class + module.offsets.class.static_fields
}

/// Returns the address of the static table of the class. This contains the
/// values of all the static fields.
pub fn get_static_table(&self, process: &Process, module: &Module) -> Option<Address> {
process
.read_pointer(self.get_static_table_pointer(module), module.pointer_size)
.ok()
.filter(|val| !val.is_null())
module
.walk()
.static_table(process, ClassRef::new(self.class))
}

/// Tries to find the parent class.
pub fn get_parent(&self, process: &Process, module: &Module) -> Option<Class> {
process
.read_pointer(
self.class + module.offsets.class.parent,
module.pointer_size,
)
.ok()
.filter(|val| !val.is_null())
.map(|class| Class { class })
module
.walk()
.parent(process, ClassRef::new(self.class))
.map(|class| Class {
class: class.address,
})
}

/// Tries to find a field with the specified name in the class. This returns
Expand Down
24 changes: 0 additions & 24 deletions src/game_engine/unity/il2cpp/field.rs

This file was deleted.

77 changes: 18 additions & 59 deletions src/game_engine/unity/il2cpp/image.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::CSTR;
use super::{Class, Module, Version};
use super::super::managed::{slot, ImageRef};
use super::{Class, Module};
use crate::{future::retry, Address, Process};

/// An image is a .NET DLL that is loaded by the game. The `Assembly-CSharp`
Expand All @@ -16,48 +16,18 @@ impl Image {
process: &'a Process,
module: &'a Module,
) -> impl DoubleEndedIterator<Item = Class> + 'a {
let type_count = process
.read::<u32>(self.image + module.offsets.image.type_count)
.unwrap_or_default() as u64;

let metadata_ptr = match (type_count, module.version) {
(0, _) => Address::NULL,
(_, Version::Base | Version::V2019) => {
self.image + module.offsets.image.metadata_handle
}
(_, _) => process
.read_pointer(
self.image + module.offsets.image.metadata_handle,
module.pointer_size,
)
.unwrap_or_default(),
};

let metadata_handle = match metadata_ptr {
Address::NULL => 0,
handle => process.read::<u32>(handle).unwrap_or_default(),
};

let type_info_definition_table = match metadata_ptr {
Address::NULL => Address::NULL,
_ => process
.read_pointer(module.type_info_definition_table, module.pointer_size)
.unwrap_or_default(),
};

let ptr = match type_info_definition_table {
Address::NULL => Address::NULL,
_ => {
type_info_definition_table + module.size_of_ptr().wrapping_mul(metadata_handle as _)
}
};

(0..type_count).filter_map(move |i| {
let walk = module.walk();
let pointer_size = walk.pointer_size;
// The runtime built by an IL2CPP module always answers the slot form.
let (slots, count) = walk
.runtime
.classes(process, pointer_size, ImageRef::new(self.image))
.slots()
.unwrap_or((Address::NULL, 0));

(0..count).filter_map(move |i| {
process
.read_pointer(
ptr + module.size_of_ptr().wrapping_mul(i),
module.pointer_size,
)
.read_pointer(slot(slots, pointer_size, i), pointer_size)
.ok()
.filter(|val| !val.is_null())
.map(|class| Class { class })
Expand All @@ -66,23 +36,12 @@ impl Image {

/// Tries to find the specified [.NET class](struct@Class) in the image.
pub fn get_class(&self, process: &Process, module: &Module, class_name: &str) -> Option<Class> {
let name_space_index = class_name.rfind('.');

self.classes(process, module).find(|class| {
class.get_name::<CSTR>(process, module).is_ok_and(|name| {
if let Some(name_space_index) = name_space_index {
let class_name_space = &class_name[..name_space_index];
let class_name = &class_name[name_space_index + 1..];

name.matches(class_name)
&& class
.get_name_space::<CSTR>(process, module)
.is_ok_and(|name_space| name_space.matches(class_name_space))
} else {
name.matches(class_name)
}
module
.walk()
.find_class(process, ImageRef::new(self.image), class_name)
.map(|class| Class {
class: class.address,
})
})
}

/// Tries to find the specified [.NET class](struct@Class) in the image.
Expand Down
Loading
Loading