Skip to content

Commit

Permalink
Some cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
CryZe committed Oct 1, 2023
1 parent fb3189b commit cbe2a34
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 97 deletions.
1 change: 1 addition & 0 deletions crates/livesplit-auto-splitting/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ edition = "2021"
[dependencies]
anyhow = { version = "1.0.45", default-features = false }
async-trait = "0.1.73"
bytemuck = { version = "1.14.0", features = ["min_const_generics"] }
proc-maps = { version = "0.3.0", default-features = false }
read-process-memory = { version = "0.1.4", default-features = false }
slotmap = { version = "1.0.2", default-features = false }
Expand Down
56 changes: 39 additions & 17 deletions crates/livesplit-auto-splitting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ pub struct Address(pub u64);
pub struct NonZeroAddress(pub NonZeroU64);

#[repr(transparent)]
pub struct ProcessId(NonZeroU64);
pub struct Process(NonZeroU64);

#[repr(transparent)]
pub struct ProcessId(u64);

#[repr(transparent)]
pub struct TimerState(u32);
Expand All @@ -56,13 +59,13 @@ pub struct MemoryRangeFlags(NonZeroU64);

impl MemoryRangeFlags {
/// The memory range is readable.
pub const READ: Self = Self(NonZeroU64::new(1 << 1).unwrap());
pub const READ: Self = Self(match NonZeroU64::new(1 << 1) { Some(v) => v, None => panic!() });
/// The memory range is writable.
pub const WRITE: Self = Self(NonZeroU64::new(1 << 2).unwrap());
pub const WRITE: Self = Self(match NonZeroU64::new(1 << 2) { Some(v) => v, None => panic!() });
/// The memory range is executable.
pub const EXECUTE: Self = Self(NonZeroU64::new(1 << 3).unwrap());
pub const EXECUTE: Self = Self(match NonZeroU64::new(1 << 3) { Some(v) => v, None => panic!() });
/// The memory range has a file path.
pub const PATH: Self = Self(NonZeroU64::new(1 << 4).unwrap());
pub const PATH: Self = Self(match NonZeroU64::new(1 << 4) { Some(v) => v, None => panic!() });
}

extern "C" {
Expand Down Expand Up @@ -98,58 +101,77 @@ extern "C" {
pub fn timer_resume_game_time();

/// Attaches to a process based on its name.
pub fn process_attach(name_ptr: *const u8, name_len: usize) -> Option<ProcessId>;
pub fn process_attach(name_ptr: *const u8, name_len: usize) -> Option<Process>;
/// Attaches to a process based on its process id.
pub fn process_attach_by_pid(pid: ProcessId);
/// Detaches from a process.
pub fn process_detach(process: ProcessId);
pub fn process_detach(process: Process);
/// Lists processes based on their name. Returns `false` if listing the
/// processes failed. If it was successful, the buffer is now filled
/// with the process ids. They are in no specific order. The
/// `list_len_ptr` will be updated to the amount of process ids that
/// were found. If this is larger than the original value provided, the
/// buffer provided was too small and not all process ids could be
/// stored. This is still considered successful and can optionally be
/// treated as an error condition by the caller by checking if the
/// length increased and potentially reallocating a larger buffer. If
/// the length decreased after the call, the buffer was larger than
/// needed and the remaining entries are untouched.
pub fn process_list_by_name(
name_ptr: *const u8,
name_len: usize,
list_ptr: *mut ProcessId,
list_len_ptr: *mut usize,
) -> bool;
/// Checks whether is a process is still open. You should detach from a
/// process and stop using it if this returns `false`.
pub fn process_is_open(process: ProcessId) -> bool;
pub fn process_is_open(process: Process) -> bool;
/// Reads memory from a process at the address given. This will write
/// the memory to the buffer given. Returns `false` if this fails.
pub fn process_read(
process: ProcessId,
process: Process,
address: Address,
buf_ptr: *mut u8,
buf_len: usize,
) -> bool;

/// Gets the address of a module in a process.
pub fn process_get_module_address(
process: ProcessId,
process: Process,
name_ptr: *const u8,
name_len: usize,
) -> Option<NonZeroAddress>;
/// Gets the size of a module in a process.
pub fn process_get_module_size(
process: ProcessId,
process: Process,
name_ptr: *const u8,
name_len: usize,
) -> Option<NonZeroU64>;

/// Gets the number of memory ranges in a given process.
pub fn process_get_memory_range_count(process: ProcessId) -> Option<NonZeroU64>;
pub fn process_get_memory_range_count(process: Process) -> Option<NonZeroU64>;
/// Gets the start address of a memory range by its index.
pub fn process_get_memory_range_address(
process: ProcessId,
process: Process,
idx: u64,
) -> Option<NonZeroAddress>;
/// Gets the size of a memory range by its index.
pub fn process_get_memory_range_size(process: ProcessId, idx: u64) -> Option<NonZeroU64>;
pub fn process_get_memory_range_size(process: Process, idx: u64) -> Option<NonZeroU64>;
/// Gets the flags of a memory range by its index.
pub fn process_get_memory_range_flags(
process: ProcessId,
process: Process,
idx: u64,
) -> Option<MemoryRangeFlags>;

/// Stores the file system path of the executable in the buffer given. The
/// path is a pa thth that is accessiblerough the WASI file system, so a
/// path is a path that is accessible through the WASI file system, so a
/// Windows path of `C:\foo\bar.exe` would be returned as
/// `/mnt/c/foo/bar.exe`. Returns `false` if the buffer is too small. After
/// this call, no matter whether it was successful or not, the
/// `buf_len_ptr` will be set to the required buffer size. The path is
/// guaranteed to be valid UTF-8 and is not nul-terminated.
pub fn process_get_path(
process: ProcessId,
process: Process,
buf_ptr: *mut u8,
buf_len_ptr: *mut usize,
) -> bool;
Expand Down
61 changes: 38 additions & 23 deletions crates/livesplit-auto-splitting/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
//! pub struct NonZeroAddress(pub NonZeroU64);
//!
//! #[repr(transparent)]
//! pub struct ProcessId(NonZeroU64);
//! pub struct Process(NonZeroU64);
//!
//! #[repr(transparent)]
//! pub struct ProcessId(u64);
//!
//! #[repr(transparent)]
//! pub struct TimerState(u32);
Expand All @@ -56,13 +59,13 @@
//!
//! impl MemoryRangeFlags {
//! /// The memory range is readable.
//! pub const READ: Self = Self(NonZeroU64::new(1 << 1).unwrap());
//! pub const READ: Self = Self(match NonZeroU64::new(1 << 1) { Some(v) => v, None => panic!() });
//! /// The memory range is writable.
//! pub const WRITE: Self = Self(NonZeroU64::new(1 << 2).unwrap());
//! pub const WRITE: Self = Self(match NonZeroU64::new(1 << 2) { Some(v) => v, None => panic!() });
//! /// The memory range is executable.
//! pub const EXECUTE: Self = Self(NonZeroU64::new(1 << 3).unwrap());
//! pub const EXECUTE: Self = Self(match NonZeroU64::new(1 << 3) { Some(v) => v, None => panic!() });
//! /// The memory range has a file path.
//! pub const PATH: Self = Self(NonZeroU64::new(1 << 4).unwrap());
//! pub const PATH: Self = Self(match NonZeroU64::new(1 << 4) { Some(v) => v, None => panic!() });
//! }
//!
//! extern "C" {
Expand Down Expand Up @@ -98,53 +101,65 @@
//! pub fn timer_resume_game_time();
//!
//! /// Attaches to a process based on its name.
//! pub fn process_attach(name_ptr: *const u8, name_len: usize) -> Option<ProcessId>;
//! /// Attaches to a process based on its pid.
//! pub fn process_attach_pid(pid: u32);
//! pub fn process_attach(name_ptr: *const u8, name_len: usize) -> Option<Process>;
//! /// Attaches to a process based on its process id.
//! pub fn process_attach_by_pid(pid: ProcessId);
//! /// Detaches from a process.
//! pub fn process_detach(process: ProcessId);
//! /// Lists processes (as pids) based on their name. Returns `false`
//! /// if the buffer is too small. After this call, no matter whether
//! /// it was successful or not, the `buf_len_ptr` will be set to the
//! /// required buffer size.
//! pub fn process_list(name_ptr: *const u8, name_len: usize, list_ptr: *mut u8, list_len_ptr: *mut usize) -> bool
//! pub fn process_detach(process: Process);
//! /// Lists processes based on their name. Returns `false` if listing the
//! /// processes failed. If it was successful, the buffer is now filled
//! /// with the process ids. They are in no specific order. The
//! /// `list_len_ptr` will be updated to the amount of process ids that
//! /// were found. If this is larger than the original value provided, the
//! /// buffer provided was too small and not all process ids could be
//! /// stored. This is still considered successful and can optionally be
//! /// treated as an error condition by the caller by checking if the
//! /// length increased and potentially reallocating a larger buffer. If
//! /// the length decreased after the call, the buffer was larger than
//! /// needed and the remaining entries are untouched.
//! pub fn process_list_by_name(
//! name_ptr: *const u8,
//! name_len: usize,
//! list_ptr: *mut ProcessId,
//! list_len_ptr: *mut usize,
//! ) -> bool;
//! /// Checks whether is a process is still open. You should detach from a
//! /// process and stop using it if this returns `false`.
//! pub fn process_is_open(process: ProcessId) -> bool;
//! pub fn process_is_open(process: Process) -> bool;
//! /// Reads memory from a process at the address given. This will write
//! /// the memory to the buffer given. Returns `false` if this fails.
//! pub fn process_read(
//! process: ProcessId,
//! process: Process,
//! address: Address,
//! buf_ptr: *mut u8,
//! buf_len: usize,
//! ) -> bool;
//!
//! /// Gets the address of a module in a process.
//! pub fn process_get_module_address(
//! process: ProcessId,
//! process: Process,
//! name_ptr: *const u8,
//! name_len: usize,
//! ) -> Option<NonZeroAddress>;
//! /// Gets the size of a module in a process.
//! pub fn process_get_module_size(
//! process: ProcessId,
//! process: Process,
//! name_ptr: *const u8,
//! name_len: usize,
//! ) -> Option<NonZeroU64>;
//!
//! /// Gets the number of memory ranges in a given process.
//! pub fn process_get_memory_range_count(process: ProcessId) -> Option<NonZeroU64>;
//! pub fn process_get_memory_range_count(process: Process) -> Option<NonZeroU64>;
//! /// Gets the start address of a memory range by its index.
//! pub fn process_get_memory_range_address(
//! process: ProcessId,
//! process: Process,
//! idx: u64,
//! ) -> Option<NonZeroAddress>;
//! /// Gets the size of a memory range by its index.
//! pub fn process_get_memory_range_size(process: ProcessId, idx: u64) -> Option<NonZeroU64>;
//! pub fn process_get_memory_range_size(process: Process, idx: u64) -> Option<NonZeroU64>;
//! /// Gets the flags of a memory range by its index.
//! pub fn process_get_memory_range_flags(
//! process: ProcessId,
//! process: Process,
//! idx: u64,
//! ) -> Option<MemoryRangeFlags>;
//!
Expand All @@ -156,7 +171,7 @@
//! /// `buf_len_ptr` will be set to the required buffer size. The path is
//! /// guaranteed to be valid UTF-8 and is not nul-terminated.
//! pub fn process_get_path(
//! process: ProcessId,
//! process: Process,
//! buf_ptr: *mut u8,
//! buf_len_ptr: *mut usize,
//! ) -> bool;
Expand Down
34 changes: 19 additions & 15 deletions crates/livesplit-auto-splitting/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ impl Process {

pub(super) fn with_pid(pid: u32, process_list: &mut ProcessList) -> Result<Self, OpenError> {
process_list.refresh();
let process = process_list.get(sysinfo::Pid::from_u32(pid)).context(ProcessDoesntExist)?;
let process = process_list
.get(sysinfo::Pid::from_u32(pid))
.context(ProcessDoesntExist)?;

let path = build_path(process.exe());

Expand All @@ -100,21 +102,14 @@ impl Process {
})
}

pub(super) fn list_pids_by_name(name: &str, process_list: &mut ProcessList) -> Result<Vec<u32>, OpenError> {
let mut result = Vec::new();

pub(super) fn list_pids_by_name<'a>(
name: &'a str,
process_list: &'a mut ProcessList,
) -> impl Iterator<Item = u32> + 'a {
process_list.refresh();
let processes = process_list.processes_by_name(name);

for process in processes {
result.push(process.pid().as_u32());
}

if result.is_empty() {
Err(OpenError::ProcessDoesntExist)
} else {
Ok(result)
}
process_list
.processes_by_name(name)
.map(|p| p.pid().as_u32())
}

pub(super) fn is_open(&mut self, process_list: &mut ProcessList) -> bool {
Expand Down Expand Up @@ -203,6 +198,15 @@ impl Process {
self.path.as_deref()
}

/// Returns the name of the executable of the process.
pub fn name(&self) -> Option<&str> {
let path = &self.path.as_deref()?;
Some(match path.rsplit_once('/') {
Some((_, name)) => name,
None => path,
})
}

fn refresh_memory_ranges(&mut self) -> Result<(), ModuleError> {
let now = Instant::now();
if now >= self.next_memory_range_check {
Expand Down
Loading

0 comments on commit cbe2a34

Please sign in to comment.