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
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ path = "src/lib.rs"
[dependencies]
cfg-if = "1.0.0"
paste = "1.0.15"
thiserror = "2.0.12"

[dev-dependencies]
rand = "0.8.5"
Expand Down
49 changes: 49 additions & 0 deletions examples/lazer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use rosu_mem::{
error::ProcessError,
process::{Process, ProcessTraits},
signature::Signature,
};
use std::str::FromStr;

fn main() -> Result<(), ProcessError> {
// Initialize a process first
let osu_process = Process::initialize("osu!", &[])?;

println!("Found a osu! lazer process");

// Initialize a signatures
let session_signature =
Signature::from_str("00 00 00 00 80 4F 12 41").unwrap();

// Scan process for pre-initialized signatures
// Be aware that osu! lazer uses usize for addresses, so we also
// explicitly defining that
// Also reading a values for lazer is bit more tidious than stable :)
let mut session_addr: usize =
osu_process.read_signature(&session_signature)?; //- 0x208;

// Read values! For simplicity willl read only a current game time
session_addr -= 0x208;

let one = (osu_process.read_i64(session_addr + 0x90)? + 0x90) as usize;
let two = (osu_process.read_i64(one)? + 0x90) as usize;
let three = (osu_process.read_i64(two)? + 0x90) as usize;
let four = (osu_process.read_i64(three)? + 0x90) as usize;
let five = (osu_process.read_i64(four)? + 0x90) as usize;
let six = (osu_process.read_i64(five)? + 0x90) as usize;
let seven = (osu_process.read_i64(six)? + 0x340) as usize;

let game_base = osu_process.read_i64(seven)? as usize;

println!("Read a game base!");

let beatmap_clock_ptr = osu_process.read_i64(game_base + 0x4d0)? as usize;
let final_clock_ptr =
osu_process.read_i64(beatmap_clock_ptr + 0x210)? as usize;

let current_time = osu_process.read_f64(final_clock_ptr + 0x30)?;

println!("Current osu time: {current_time}");

Ok(())
}
39 changes: 39 additions & 0 deletions examples/stable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use rosu_mem::{
error::ProcessError,
process::{Process, ProcessTraits},
signature::Signature,
};
use std::str::FromStr;

// Exclude words, basically a hack to properly find a osu! process when using wine
static EXCLUDE_WORDS: [&str; 2] = ["umu-run", "waitforexitandrun"];

fn main() -> Result<(), ProcessError> {
// Initialize a process first
let osu_process = Process::initialize("osu!.exe", &EXCLUDE_WORDS)?;

println!("Found a osu! process");

// Initialize a signatures
let base_signature = Signature::from_str("F8 01 74 04 83 65").unwrap();
let status_signature = Signature::from_str("48 83 F8 04 73 1E").unwrap();

// Scan process for pre-initialized signatures
// Be aware that osu! stable uses i32 for addresses, so we also
// explicitly defining that

// Reading a base signature just to be sure that we are in the correct osu! process
let _base: i32 = osu_process.read_signature(&base_signature)?;
let status: i32 = osu_process.read_signature(&status_signature)?;

println!("Found all required signatures!");

// Now read the values that you are intrested in :)
// For the sake of keeping example simple we will read a current game state
let status_ptr = osu_process.read_i32(status - 0x4)?;
let osu_state_status = osu_process.read_u32(status_ptr)?;

println!("Current osu! game status: {osu_state_status}");

Ok(())
}
22 changes: 22 additions & 0 deletions src/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pub enum Address {
Native(usize),
I64(i64),
I32(i32)
}

fn test_convert<T: TryInto<usize>>(value: T) {

let end_value: usize = value.try_into().unwrap();

println!("")
}

fn test_xd() {
let value_i32 = 1337i32;
let value_i64 = 1338i64;
let value_usize = 1339usize;


test_convert(value_i64);

}
147 changes: 25 additions & 122 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
use std::{
error::Error, fmt::Display, num::ParseIntError, str::Utf8Error,
string::FromUtf8Error,
};
use std::{num::ParseIntError, string::FromUtf8Error};

#[derive(Debug)]
#[derive(thiserror::Error, Debug)]
pub enum ProcessError {
#[error("process not found")]
ProcessNotFound,
#[error("executable path not found")]
ExecutablePathNotFound,
#[error("not enough permissions to run, please run as admin/sudo")]
NotEnoughPermissions,
IoError {
inner: std::io::Error,
},
#[error("io error")]
IoError(#[from] std::io::Error),
#[error("failed to convert bytes to string")]
FromUtf8Error,
#[error("failed to convert type")]
ConvertionError,
#[error("trying to read bad address, addr: {0:X}, len: {1:X}")]
BadAddress(usize, usize),
#[error("cannot find signature: {0}")]
SignatureNotFound(String),
OsError {
#[cfg(target_os = "linux")]
inner: nix::errno::Errno,
#[cfg(target_os = "windows")]
inner: windows::core::Error,
},
}
#[error("failed to convert address to usize")]
AddressConvertError,

impl From<std::io::Error> for ProcessError {
fn from(value: std::io::Error) -> Self {
Self::IoError { inner: value }
}
#[cfg(target_os = "linux")]
#[error("os error `{0}`")]
OsError(#[from] nix::errno::Errno),
#[cfg(target_os = "windows")]
#[error("os error `{0}`")]
OsError(#[from] windows::core::Error),
}

impl From<std::num::ParseIntError> for ProcessError {
Expand All @@ -35,119 +35,22 @@ impl From<std::num::ParseIntError> for ProcessError {
}
}

impl From<std::num::TryFromIntError> for ProcessError {
fn from(_: std::num::TryFromIntError) -> Self {
Self::ConvertionError
}
}

impl From<FromUtf8Error> for ProcessError {
fn from(_: FromUtf8Error) -> Self {
Self::FromUtf8Error
}
}

impl From<Utf8Error> for ProcessError {
fn from(_: Utf8Error) -> Self {
impl From<std::str::Utf8Error> for ProcessError {
fn from(_: std::str::Utf8Error) -> Self {
Self::FromUtf8Error
}
}

// Linux only
#[cfg(target_os = "linux")]
impl From<nix::errno::Errno> for ProcessError {
fn from(inner: nix::errno::Errno) -> Self {
match inner {
nix::errno::Errno::EPERM => Self::NotEnoughPermissions,
nix::errno::Errno::ESRCH => Self::ProcessNotFound,
_ => Self::OsError { inner },
}
}
}

// Windows only
#[cfg(target_os = "windows")]
impl From<windows::core::Error> for ProcessError {
fn from(inner: windows::core::Error) -> Self {
Self::OsError { inner } // TODO add code value
}
}

impl Display for ProcessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ProcessError::ProcessNotFound => write!(f, "Process not found!"),
ProcessError::IoError { .. } => write!(f, "Got I/O error!"),
ProcessError::FromUtf8Error => {
write!(f, "Got Error when converting bytes to string!")
}
ProcessError::ConvertionError => {
write!(f, "Got error during type convertion")
}
ProcessError::SignatureNotFound(v) => {
write!(f, "Cannot find signature {}", v)
}
ProcessError::OsError { .. } => write!(f, "Got OS error"),
ProcessError::NotEnoughPermissions => {
write!(f, "Not enough permissions to run, please run as sudo")
}
ProcessError::BadAddress(addr, len) => {
let _ = writeln!(f, "Trying to read bad address");
writeln!(f, "Address: {:X}, Length: {:X}", addr, len)
}
ProcessError::ExecutablePathNotFound => {
write!(f, "Executable path not found!")
}
}
}
}

impl std::error::Error for ProcessError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
ProcessError::ProcessNotFound => None,
ProcessError::ExecutablePathNotFound => None,
ProcessError::NotEnoughPermissions => None,
ProcessError::IoError { inner } => Some(inner),
ProcessError::FromUtf8Error => None,
ProcessError::ConvertionError => None,
ProcessError::SignatureNotFound(_) => None,
ProcessError::OsError { inner } => Some(inner),
ProcessError::BadAddress(..) => None,
}
}
}

#[derive(Debug)]
#[derive(thiserror::Error, Debug)]
pub enum ParseSignatureError {
#[error("invalid string length `{0}`")]
InvalidLength(usize),
InvalidInt { inner: ParseIntError },
}

impl From<ParseIntError> for ParseSignatureError {
fn from(inner: ParseIntError) -> Self {
Self::InvalidInt { inner }
}
}

impl Error for ParseSignatureError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
ParseSignatureError::InvalidLength(_) => None,
ParseSignatureError::InvalidInt { inner } => Some(inner),
}
}
}

impl Display for ParseSignatureError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseSignatureError::InvalidLength(len) => {
write!(f, "Invalid string length {len}")
}
ParseSignatureError::InvalidInt { .. } => {
f.write_str("Failed to parse integer")
}
}
}
#[error("failed to parse integer")]
InvalidInt(#[from] ParseIntError),
}
Loading