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
8 changes: 8 additions & 0 deletions examples/beatmap3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,13 @@ fn main() -> Result<(), Error> {
Ok(star_rating) => println!("Current beatmap star rating: {star_rating:?}"),
Err(e) => println!("Error: {e:?}"),
}
match beatmap_reader.info() {
Ok(info) => println!("Current beatmap info: {info:?}"),
Err(e) => println!("Error: {e:?}"),
}
match beatmap_reader.stats() {
Ok(stats) => println!("Current beatmap stats: {stats:?}"),
Err(e) => println!("Error: {e:?}"),
}
Ok(())
}
4 changes: 0 additions & 4 deletions examples/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ fn main() -> Result<(), Error> {
Ok(level) => println!("Current level: {level}"),
Err(e) => println!("Error: {e:?}"),
}
match user_reader.playmode() {
Ok(playmode) => println!("Current playmode: {playmode}"),
Err(e) => println!("Error: {e:?}"),
}

Ok(())
}
48 changes: 32 additions & 16 deletions src/reader/beatmap/stable/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,19 @@ generate_offset_getter! {

pub fn stats(p: &Process, state: &mut State) -> Result<BeatmapStats, Error> {
let beatmap_addr = beatmap_addr(p, state)?;
// faster than using read_fn because we dont need to reload addr everytime
let mut buffer = [0u8; size_of::<f32>() * 4];
p.read(
beatmap_addr + 0x2c,
size_of::<f32>() * 4,
&mut buffer,
)?;


Ok(BeatmapStats {
ar: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.ar)?,
od: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.od)?,
cs: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.cs)?,
hp: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.hp)?,
ar: f32::from_le_bytes(buffer[0..4].try_into().unwrap()),
cs: f32::from_le_bytes(buffer[4..8].try_into().unwrap()),
hp: f32::from_le_bytes(buffer[8..12].try_into().unwrap()),
od: f32::from_le_bytes(buffer[12..].try_into().unwrap()),
length: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.total_length)?,
star_rating: crate::reader::beatmap::stable::file::star_rating(p, state)?,
object_count: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.object_count)?,
Expand All @@ -53,7 +60,25 @@ pub fn stats(p: &Process, state: &mut State) -> Result<BeatmapStats, Error> {

pub fn info(p: &Process, state: &mut State) -> Result<BeatmapInfo, Error> {
let beatmap_addr = beatmap_addr(p, state)?;
// done like that to be more efficient reading the string one by one would need to reload addr everytime which cost more

let mut buffer = [0u8; size_of::<f32>() * 4];
p.read(
beatmap_addr + 0x2c,
size_of::<f32>() * 4,
&mut buffer,
)?;


let stats =BeatmapStats {
ar: f32::from_le_bytes(buffer[0..4].try_into().unwrap()),
cs: f32::from_le_bytes(buffer[4..8].try_into().unwrap()),
hp: f32::from_le_bytes(buffer[8..12].try_into().unwrap()),
od: f32::from_le_bytes(buffer[12..].try_into().unwrap()),
length: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.total_length)?,
star_rating: crate::reader::beatmap::stable::file::star_rating(p, state)?,
object_count: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.object_count)?,
slider_count: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.slider_count)?,
};
Ok(BeatmapInfo {
technical: BeatmapTechnicalInfo {
md5: p.read_string(beatmap_addr + BEATMAP_OFFSET.technical.md5)?,
Expand All @@ -73,16 +98,7 @@ pub fn info(p: &Process, state: &mut State) -> Result<BeatmapInfo, Error> {
difficulty: p.read_string(beatmap_addr + BEATMAP_OFFSET.metadata.difficulty)?,
tags: p.read_string(beatmap_addr + BEATMAP_OFFSET.metadata.tags)?,
},
stats: BeatmapStats {
ar: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.ar)?,
od: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.od)?,
cs: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.cs)?,
hp: p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.hp)?,
length: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.total_length)?,
star_rating: crate::reader::beatmap::stable::file::star_rating(p, state)?,
object_count: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.object_count)?,
slider_count: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.slider_count)?,
},
stats,
location: BeatmapLocation {
folder: p.read_string(beatmap_addr + BEATMAP_OFFSET.location.folder)?,
filename: p.read_string(beatmap_addr + BEATMAP_OFFSET.location.filename)?,
Expand Down
6 changes: 3 additions & 3 deletions src/reader/beatmap/stable/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ pub(crate) const BEATMAP_LOCATION_OFFSET: BeatmapLocationOffset = BeatmapLocatio

pub(crate) const BEATMAP_STATS_OFFSET: BeatmapStatsOffset = BeatmapStatsOffset {
ar: 0x2c,
od: 0x30,
cs: 0x34,
hp: 0x38,
cs: 0x30,
hp: 0x34,
od: 0x38,
object_count: 0xf8,
total_length: 0x134,
drain_time: 0x0, // TODO
Expand Down
55 changes: 39 additions & 16 deletions src/reader/gameplay/stable/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
generate_offset_getter,
reader::helpers::{read_f64, read_i16, read_i32, read_string, read_u64},
};
use std::mem::size_of;
use rosu_mem::process::{Process, ProcessTraits};

pub fn rulesets_addr(p: &Process, state: &mut State) -> Result<i32, Error> {
Expand Down Expand Up @@ -60,15 +61,24 @@ pub fn retries(p: &Process, state: &mut State) -> Result<i32, Error> {

pub fn hits(p: &Process, state: &mut State) -> Result<Hit, Error> {
let score_base = score_base(p, state)?;
// TODO: check issue for reading the full block and
// separating bits

// Read all hits data in one memory operation
let mut hits_buffer = [0u8; size_of::<i16>() * 6];
p.read(
score_base + GAMEPLAY_OFFSET.hits._100,
size_of::<i16>() * 6,
&mut hits_buffer,
)?;

// Safety: unwrap here because buffer is already initialized and filled
// with zeros, the worst case scenario is hits going to be zeros
Ok(Hit {
_300: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._300)?,
_100: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._100)?,
_50: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._50)?,
_miss: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._miss)?,
_geki: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._geki)?,
_katu: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._katu)?,
_100: i16::from_le_bytes(hits_buffer[0..2].try_into().unwrap()),
_300: i16::from_le_bytes(hits_buffer[2..4].try_into().unwrap()),
_50: i16::from_le_bytes(hits_buffer[4..6].try_into().unwrap()),
_geki: i16::from_le_bytes(hits_buffer[6..8].try_into().unwrap()),
_katu: i16::from_le_bytes(hits_buffer[8..10].try_into().unwrap()),
_miss: i16::from_le_bytes(hits_buffer[10..12].try_into().unwrap()),
})
}

Expand All @@ -78,6 +88,26 @@ pub fn info(p: &Process, state: &mut State) -> Result<GameplayInfo, Error> {
let hp = hp(p, state)?;
let mods = mods(p, state)?;


// Read all hits data in one memory operation
let mut hits_buffer = [0u8; size_of::<i16>() * 6];
p.read(
score_base + GAMEPLAY_OFFSET.hits._100,
size_of::<i16>() * 6,
&mut hits_buffer,
)?;

// Safety: unwrap here because buffer is already initialized and filled
// with zeros, the worst case scenario is hits going to be zeros
let hits = Hit {
_100: i16::from_le_bytes(hits_buffer[0..2].try_into().unwrap()),
_300: i16::from_le_bytes(hits_buffer[2..4].try_into().unwrap()),
_50: i16::from_le_bytes(hits_buffer[4..6].try_into().unwrap()),
_geki: i16::from_le_bytes(hits_buffer[6..8].try_into().unwrap()),
_katu: i16::from_le_bytes(hits_buffer[8..10].try_into().unwrap()),
_miss: i16::from_le_bytes(hits_buffer[10..12].try_into().unwrap()),
};

Ok(GameplayInfo {
score: p.read_i32(score_base + GAMEPLAY_OFFSET.score)?,
mods,
Expand All @@ -87,13 +117,6 @@ pub fn info(p: &Process, state: &mut State) -> Result<GameplayInfo, Error> {
username: p.read_string(score_base + GAMEPLAY_OFFSET.username)?,
ig_time: game_time(p, state)?, // different base
retries: retries(p, state)?, // different base
hits: Hit {
_300: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._300)?,
_100: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._100)?,
_50: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._50)?,
_miss: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._miss)?,
_geki: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._geki)?,
_katu: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._katu)?,
},
hits,
})
}
12 changes: 6 additions & 6 deletions src/reader/gameplay/stable/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ pub struct GameplayHitsOffset {
}

pub const GAMEPLAY_HITS_OFFSET: GameplayHitsOffset = GameplayHitsOffset {
_300: 0x8a,
_100: 0x88,
_50: 0x8c,
_miss: 0x92,
_geki: 0x8e,
_katu: 0x90,
_100: 0x88, // 136
_300: 0x8A, // 138
_50: 0x8C, // 140
_geki: 0x8E, // 142
_katu: 0x90, // 144
_miss: 0x92 // 146
};
22 changes: 16 additions & 6 deletions src/reader/resultscreen/stable/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@ pub fn result_screen_ptr(p: &Process, state: &mut State) -> Result<i32, Error> {

pub fn hits(p: &Process, state: &mut State) -> Result<Hit, Error> {
let score_base = result_screen_base(p, state)?;
// Read all hits data in one memory operation
let mut hits_buffer = [0u8; size_of::<i16>() * 6];
p.read(
score_base + RESULT_SCREEN_OFFSET.hits._100,
size_of::<i16>() * 6,
&mut hits_buffer,
)?;

// Safety: unwrap here because buffer is already initialized and filled
// with zeros, the worst case scenario is hits going to be zeros
Ok(Hit {
_300: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._300)?,
_100: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._100)?,
_50: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._50)?,
_miss: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._miss)?,
_geki: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._geki)?,
_katu: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._katu)?,
_100: i16::from_le_bytes(hits_buffer[0..2].try_into().unwrap()),
_300: i16::from_le_bytes(hits_buffer[2..4].try_into().unwrap()),
_50: i16::from_le_bytes(hits_buffer[4..6].try_into().unwrap()),
_geki: i16::from_le_bytes(hits_buffer[6..8].try_into().unwrap()),
_katu: i16::from_le_bytes(hits_buffer[8..10].try_into().unwrap()),
_miss: i16::from_le_bytes(hits_buffer[10..12].try_into().unwrap()),
})
}

Expand Down
12 changes: 6 additions & 6 deletions src/reader/resultscreen/stable/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ pub struct ResultScreenHitsOffset {
}

pub(crate) const RESULT_SCREEN_HITS_OFFSET: ResultScreenHitsOffset = ResultScreenHitsOffset {
_300: 0x8A,
_100: 0x88,
_50: 0x8C,
_miss: 0x92,
_geki: 0x8E,
_katu: 0x90,
_100: 0x88, // 136
_300: 0x8A, // 138
_50: 0x8C, // 140
_geki: 0x8E, // 142
_katu: 0x90, // 144
_miss: 0x92 // 146
};
22 changes: 17 additions & 5 deletions src/reader/user/stable/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::reader::user::common::UserInfo;
use crate::reader::user::stable::offset::USER_PROFILE_OFFSET;
use crate::Error;
use rosu_mem::process::{Process, ProcessTraits};
use std::mem::size_of;

pub fn user_base(p: &Process, state: &mut State) -> Result<i32, Error> {
Ok(p.read_i32(p.read_i32(state.addresses.user_profile + USER_PROFILE_OFFSET.ptr)?)?)
Expand All @@ -25,19 +26,30 @@ generate_offset_getter! {

pub fn info(p: &Process, state: &mut State) -> Result<UserInfo, Error> {
let user_profile_base = user_base(p, state)?;
let mut buffer = [0u8; size_of::<i32>() * 5];
p.read(
user_profile_base + USER_PROFILE_OFFSET.playcount,
size_of::<i32>() * 5,
&mut buffer,
)?;
let playcount = i32::from_le_bytes(buffer[0..4].try_into().unwrap());
let playmode = i32::from_le_bytes(buffer[4..8].try_into().unwrap());
let rank = i32::from_le_bytes(buffer[8..12].try_into().unwrap());
let pp = i32::from_le_bytes(buffer[12..16].try_into().unwrap());
let bancho_status = i32::from_le_bytes(buffer[16..20].try_into().unwrap());

let user_profile = UserInfo {
id: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.id)?,
username: p.read_string(user_profile_base + USER_PROFILE_OFFSET.username)?,
pp: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.pp)?,
pp,
rankedscore: p.read_i64(user_profile_base + USER_PROFILE_OFFSET.rankedscore)?,
level: p.read_f32(user_profile_base + USER_PROFILE_OFFSET.level)?,
playcount: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.playcount)?,
rank: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.rank)?,
playmode: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.playmode)?,
playcount,
rank,
playmode,
accuracy: p.read_f64(user_profile_base + USER_PROFILE_OFFSET.accuracy)?,
country_code: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.country_code)?,
bancho_status: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.bancho_status)?,
bancho_status,
};
Ok(user_profile)
}
15 changes: 8 additions & 7 deletions src/reader/user/stable/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ pub struct UserProfileOffset {

pub(crate) const USER_PROFILE_OFFSET: UserProfileOffset = UserProfileOffset {
ptr: 0x7,
id: 0x70,
username: 0x30,
pp: 0x88,
accuracy: 0x4,
rankedscore: 0xC,
username: 0x30,
id: 0x70,
level: 0x74,
playcount: 0x7C,
rank: 0x84,
playcount: 0x7C, // start read
playmode: 0x80,
accuracy: 0x4,
rank: 0x84,
pp: 0x88,
bancho_status: 0x8c, // end read
country_code: 0x9c,
bancho_status: 0x8c,

};
Loading