From 2ab8aeff9f166aaea0a2c07fc8e6a0d4e2f0cf40 Mon Sep 17 00:00:00 2001 From: Glubus <50545507+Glubus@users.noreply.github.com> Date: Sun, 13 Jul 2025 01:11:51 +0200 Subject: [PATCH 1/6] refactor: now full use of potential macros :) only for beatmap for now --- examples/beatmap1.rs | 4 +- examples/beatmap2.rs | 2 +- examples/beatmap3.rs | 114 +++++++++++++ examples/pp.rs | 4 +- src/reader/beatmap/mod.rs | 131 ++++++++------ src/reader/beatmap/stable/file.rs | 237 +++++++++++--------------- src/reader/beatmap/stable/location.rs | 20 --- src/reader/beatmap/stable/memory.rs | 135 +++++---------- src/reader/beatmap/stable/mod.rs | 12 +- src/reader/helpers.rs | 37 ++++ src/reader/mod.rs | 1 + 11 files changed, 382 insertions(+), 315 deletions(-) create mode 100644 examples/beatmap3.rs delete mode 100644 src/reader/beatmap/stable/location.rs create mode 100644 src/reader/helpers.rs diff --git a/examples/beatmap1.rs b/examples/beatmap1.rs index e22e183..0715df9 100644 --- a/examples/beatmap1.rs +++ b/examples/beatmap1.rs @@ -1,12 +1,12 @@ use rosu_memory_lib::init_loop; -use rosu_memory_lib::reader::beatmap::stable::memory::get_beatmap_info; +use rosu_memory_lib::reader::beatmap::stable::file::info; use rosu_memory_lib::Error; fn main() -> Result<(), Error> { let (mut state, process) = init_loop(500)?; println!("Successfully initialized!"); loop { - match get_beatmap_info(&process, &mut state) { + match info(&process, &mut state) { Ok(beatmap_info) => println!("Current beatmap info: {beatmap_info:?}"), Err(e) => println!("Error: {e:?}"), } diff --git a/examples/beatmap2.rs b/examples/beatmap2.rs index c1e2b7a..ca3e75d 100644 --- a/examples/beatmap2.rs +++ b/examples/beatmap2.rs @@ -7,7 +7,7 @@ fn main() -> Result<(), Error> { let (mut state, process) = init_loop(500)?; let mut beatmap_reader = BeatmapReader::new(&process, &mut state, OsuClientKind::Stable)?; loop { - match beatmap_reader.get_beatmap_info() { + match beatmap_reader.info() { Ok(beatmap_info) => println!("Current beatmap info: {beatmap_info:?}"), Err(e) => println!("Error: {e:?}"), } diff --git a/examples/beatmap3.rs b/examples/beatmap3.rs new file mode 100644 index 0000000..15c0089 --- /dev/null +++ b/examples/beatmap3.rs @@ -0,0 +1,114 @@ +use rosu_memory_lib::init_loop; +use rosu_memory_lib::reader::beatmap::BeatmapReader; +use rosu_memory_lib::reader::common::OsuClientKind; +use rosu_memory_lib::Error; + +fn main() -> Result<(), Error> { + let (mut state, process) = init_loop(500)?; + let mut beatmap_reader = BeatmapReader::new(&process, &mut state, OsuClientKind::Stable)?; + match beatmap_reader.audio_path() { + Ok(audio_path) => println!("Current beatmap audio path: {audio_path:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.path() { + Ok(path) => println!("Current beatmap path: {path:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.md5() { + Ok(md5) => println!("Current beatmap md5: {md5:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.id() { + Ok(id) => println!("Current beatmap id: {id:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.set_id() { + Ok(set_id) => println!("Current beatmap set id: {set_id:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.mode() { + Ok(mode) => println!("Current beatmap mode: {mode:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.tags() { + Ok(tags) => println!("Current beatmap tags: {tags:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.length() { + Ok(length) => println!("Current beatmap length: {length:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.drain_time() { + Ok(drain_time) => println!("Current beatmap drain time: {drain_time:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.author() { + Ok(author) => println!("Current beatmap author: {author:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.creator() { + Ok(creator) => println!("Current beatmap creator: {creator:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.title_romanized() { + Ok(title_romanized) => println!("Current beatmap title romanized: {title_romanized:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.title() { + Ok(title) => println!("Current beatmap title: {title:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.difficulty() { + Ok(difficulty) => println!("Current beatmap difficulty: {difficulty:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.od() { + Ok(od) => println!("Current beatmap od: {od:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.ar() { + Ok(ar) => println!("Current beatmap ar: {ar:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.cs() { + Ok(cs) => println!("Current beatmap cs: {cs:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.hp() { + Ok(hp) => println!("Current beatmap hp: {hp:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.object_count() { + Ok(object_count) => println!("Current beatmap object count: {object_count:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.slider_count() { + Ok(slider_count) => println!("Current beatmap slider count: {slider_count:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.folder() { + Ok(folder) => println!("Current beatmap folder: {folder:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.filename() { + Ok(filename) => println!("Current beatmap filename: {filename:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.audio() { + Ok(audio) => println!("Current beatmap audio: {audio:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.cover() { + Ok(cover) => println!("Current beatmap cover: {cover:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.status() { + Ok(status) => println!("Current beatmap status: {status:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.star_rating() { + Ok(star_rating) => println!("Current beatmap star rating: {star_rating:?}"), + Err(e) => println!("Error: {e:?}"), + } + Ok(()) +} diff --git a/examples/pp.rs b/examples/pp.rs index f0e21e4..65835aa 100644 --- a/examples/pp.rs +++ b/examples/pp.rs @@ -1,6 +1,6 @@ use rosu_mem::process::Process; use rosu_memory_lib::init_loop; -use rosu_memory_lib::reader::beatmap::stable::file::get_beatmap_path; +use rosu_memory_lib::reader::beatmap::stable::file::path; use rosu_memory_lib::reader::common::stable::memory::get_menu_mods; use rosu_memory_lib::reader::structs::State; use rosu_memory_lib::Error; @@ -99,7 +99,7 @@ fn process_game_state( calc_state: &mut CalculatorState, ) -> Result<(), Error> { let mut mods_changed = false; - match get_beatmap_path(process, state) { + match path(process, state) { Ok(beatmap_path) => { // Update mods if they changed if let Ok(new_mods) = get_menu_mods(process, state) { diff --git a/src/reader/beatmap/mod.rs b/src/reader/beatmap/mod.rs index 44898d7..346b3d3 100644 --- a/src/reader/beatmap/mod.rs +++ b/src/reader/beatmap/mod.rs @@ -32,91 +32,126 @@ impl<'a> BeatmapReader<'a> { }) } - pub fn get_beatmap_info(&mut self) -> Result { + pub fn info(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_info(self.process, self.state), + OsuClientKind::Stable => stable::memory::info(self.process, self.state), + _ => Err(Error::Unsupported( + "Unsupported osu type for now".to_string(), + )), + } + } + pub fn cover(&mut self) -> Result { + match self.osu_type { + OsuClientKind::Stable => stable::memory::cover(self.process, self.state), + _ => Err(Error::Unsupported( + "Unsupported osu type for now".to_string(), + )), + } + } + + pub fn path(&mut self) -> Result { + match self.osu_type { + OsuClientKind::Stable => stable::file::path(self.process, self.state), + _ => Err(Error::Unsupported( + "Unsupported osu type for now".to_string(), + )), + } + } + + pub fn folder(&mut self) -> Result { + match self.osu_type { + OsuClientKind::Stable => stable::memory::folder(self.process, self.state), + _ => Err(Error::Unsupported( + "Unsupported osu type for now".to_string(), + )), + } + } + + pub fn filename(&mut self) -> Result { + match self.osu_type { + OsuClientKind::Stable => stable::memory::filename(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_path(&mut self) -> Result { + pub fn audio(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::file::get_beatmap_path(self.process, self.state), + OsuClientKind::Stable => stable::memory::audio(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_audio_path(&mut self) -> Result { + pub fn audio_path(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::file::get_audio_path(self.process, self.state), + OsuClientKind::Stable => stable::file::audio_path(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_md5(&mut self) -> Result { + pub fn md5(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_md5(self.process, self.state), + OsuClientKind::Stable => stable::memory::md5(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_id(&mut self) -> Result { + pub fn id(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_id(self.process, self.state), + OsuClientKind::Stable => stable::memory::id(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_set_id(&mut self) -> Result { + pub fn set_id(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_set_id(self.process, self.state), + OsuClientKind::Stable => stable::memory::set_id(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_mode(&mut self) -> Result { + pub fn mode(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_mode(self.process, self.state), + OsuClientKind::Stable => stable::memory::mode(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_tags(&mut self) -> Result { + pub fn tags(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_tags(self.process, self.state), + OsuClientKind::Stable => stable::memory::tags(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_length(&mut self) -> Result { + pub fn length(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_length(self.process, self.state), + OsuClientKind::Stable => stable::memory::length(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_drain_time(&mut self) -> Result { + pub fn drain_time(&mut self) -> Result { match self.osu_type { OsuClientKind::Stable => { - stable::memory::get_beatmap_drain_time(self.process, self.state) + stable::memory::drain_time(self.process, self.state) } _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), @@ -124,100 +159,100 @@ impl<'a> BeatmapReader<'a> { } } - pub fn get_beatmap_status(&mut self) -> Result { + pub fn status(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_status(self.process, self.state), + OsuClientKind::Stable => stable::memory::status(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_author(&mut self) -> Result { + pub fn author(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_author(self.process, self.state), + OsuClientKind::Stable => stable::memory::author(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_creator(&mut self) -> Result { + pub fn creator(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_creator(self.process, self.state), + OsuClientKind::Stable => stable::memory::creator(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_title_romanized(&mut self) -> Result { + pub fn title_romanized(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_title_romanized(self.process, self.state), + OsuClientKind::Stable => stable::memory::title_romanized(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_title_original(&mut self) -> Result { + pub fn title(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_title_original(self.process, self.state), + OsuClientKind::Stable => stable::memory::title(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_difficulty(&mut self) -> Result { + pub fn difficulty(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_difficulty(self.process, self.state), + OsuClientKind::Stable => stable::memory::difficulty(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_od(&mut self) -> Result { + pub fn od(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_od(self.process, self.state), + OsuClientKind::Stable => stable::memory::od(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_ar(&mut self) -> Result { + pub fn ar(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_ar(self.process, self.state), + OsuClientKind::Stable => stable::memory::ar(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_cs(&mut self) -> Result { + pub fn cs(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_cs(self.process, self.state), + OsuClientKind::Stable => stable::memory::cs(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_hp(&mut self) -> Result { + pub fn hp(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_hp(self.process, self.state), + OsuClientKind::Stable => stable::memory::hp(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_beatmap_object_count(&mut self) -> Result { + pub fn object_count(&mut self) -> Result { match self.osu_type { OsuClientKind::Stable => { - stable::memory::get_beatmap_object_count(self.process, self.state) + stable::memory::object_count(self.process, self.state) } _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), @@ -225,10 +260,10 @@ impl<'a> BeatmapReader<'a> { } } - pub fn get_beatmap_slider_count(&mut self) -> Result { + pub fn slider_count(&mut self) -> Result { match self.osu_type { OsuClientKind::Stable => { - stable::memory::get_beatmap_slider_count(self.process, self.state) + stable::memory::slider_count(self.process, self.state) } _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), @@ -236,10 +271,10 @@ impl<'a> BeatmapReader<'a> { } } - pub fn get_beatmap_star_rating(&mut self) -> Result { + pub fn star_rating(&mut self) -> Result { match self.osu_type { OsuClientKind::Stable => { - stable::file::get_beatmap_star_rating(self.process, self.state) + stable::file::star_rating(self.process, self.state) } _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), @@ -247,9 +282,9 @@ impl<'a> BeatmapReader<'a> { } } - pub fn get_beatmap_stats(&mut self) -> Result { + pub fn stats(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_beatmap_stats(self.process, self.state), + OsuClientKind::Stable => stable::memory::stats(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), diff --git a/src/reader/beatmap/stable/file.rs b/src/reader/beatmap/stable/file.rs index f67b44b..aeae25e 100644 --- a/src/reader/beatmap/stable/file.rs +++ b/src/reader/beatmap/stable/file.rs @@ -5,8 +5,8 @@ use crate::reader::beatmap::common::{ BeatmapInfo, BeatmapLocation, BeatmapMetadata, BeatmapStarRating, BeatmapStats, BeatmapStatus, BeatmapTechnicalInfo, }; -use crate::reader::beatmap::stable::location::{get_audio, get_filename, get_folder}; -use crate::reader::beatmap::stable::{get_beatmap_addr, offset::BEATMAP_OFFSET}; +use crate::reader::beatmap::stable::memory::{audio, filename, folder}; +use crate::reader::beatmap::stable::{beatmap_addr, offset::BEATMAP_OFFSET}; use crate::reader::common::stable::memory::get_path_folder; use crate::reader::structs::State; use crate::Error; @@ -14,150 +14,113 @@ use rosu_map::section::hit_objects::HitObjectKind; use rosu_map::Beatmap as RmBeatmap; use rosu_mem::process::{Process, ProcessTraits}; -pub fn get_beatmap_path(p: &Process, state: &mut State) -> Result { - let folder = get_folder(p, state)?; - let filename = get_filename(p, state)?; +pub fn path(p: &Process, state: &mut State) -> Result { + let folder = folder(p, state)?; + let filename = filename(p, state)?; let songs_path = get_path_folder(p, state)?; Ok(songs_path.join(folder).join(filename)) } -pub fn get_audio_path(p: &Process, state: &mut State) -> Result { - let folder = get_folder(p, state)?; - let audio = get_audio(p, state)?; +pub fn audio_path(p: &Process, state: &mut State) -> Result { + let folder = folder(p, state)?; + let audio = audio(p, state)?; let songs_path = get_path_folder(p, state)?; Ok(songs_path.join(folder).join(audio)) } -pub fn get_beatmap_md5(p: &Process, state: &mut State) -> Result { - // TODO: implement this for now will get from memory - crate::reader::beatmap::stable::memory::get_beatmap_md5(p, state) -} - -pub fn get_beatmap_id(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.beatmap_id) -} - -pub fn get_beatmap_set_id(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.beatmap_set_id) -} - -pub fn get_beatmap_mode(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(GameMode::from(b.mode as u32)) -} - -pub fn get_beatmap_tags(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.tags) -} -pub fn get_beatmap_length(p: &Process, state: &mut State) -> Result { - // implement this later for now will get from memory - crate::reader::beatmap::stable::memory::get_beatmap_length(p, state) -} - -pub fn get_beatmap_drain_time(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - - if b.hit_objects.is_empty() { - return Ok(0); +// generate getters that use the default logic +macro_rules! generate_beatmap_field_getter { + ( + $( $fn_name:ident : $ret_ty:ty = $field:ident ; )* + ) => { + $( + pub fn $fn_name(p: &Process, state: &mut State) -> Result<$ret_ty, Error> { + let path = path(p, state)?; + let b = RmBeatmap::from_path(path)?; + Ok(b.$field) + } + )* + }; +} +// generate getters that use custom logic +macro_rules! generate_beatmap_custom_getter_safe { + ( + $( $fn_name:ident : $ret_ty:ty = |$b:ident| $body:block )* + ) => { + $( + pub fn $fn_name(p: &Process, state: &mut State) -> Result<$ret_ty, Error> { + let path = path(p, state)?; + let $b = RmBeatmap::from_path(path)?; + $body + } + )* + }; +} + +generate_beatmap_field_getter! { + beatmap_id: i32 = beatmap_id; + beatmap_set_id: i32 = beatmap_set_id; + author: String = artist; + creator: String = creator; + title_romanized: String = title; + title: String = title_unicode; + difficulty: String = version; + tags: String = tags; + od: f32 = overall_difficulty; + ar: f32 = approach_rate; + cs: f32 = circle_size; + hp: f32 = hp_drain_rate; +} + +generate_beatmap_custom_getter_safe! { + slider_count: i32 = |b| { + Ok(b.hit_objects.iter() + .filter(|h| matches!(h.kind, HitObjectKind::Slider(_))) + .count() as i32) } + object_count: u32 = |b| { + Ok(b.hit_objects.len() as u32) + } + total_length: i32 = |b| { + let last = b.hit_objects.last().ok_or_else(|| Error::Other("Empty hitobject list".into()))?; - // SAFETY: Checked bounds above - // TODO: That's misleading because last note can be LN or slider - // which have duration beyond start_time - let drain_time = - b.hit_objects.last().unwrap().start_time - b.hit_objects.first().unwrap().start_time; - - Ok(drain_time as i32) -} - -pub fn get_beatmap_status(p: &Process, state: &mut State) -> Result { - // cant do this in file mode - crate::reader::beatmap::stable::memory::get_beatmap_status(p, state) -} - -pub fn get_author(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.artist) -} - -pub fn get_creator(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.creator) -} - -pub fn get_title_romanized(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.title) -} - -pub fn get_title_original(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.title_unicode) -} - -pub fn get_difficulty(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.version) -} + let duration = match &last.kind { + HitObjectKind::Hold(hold_data) => last.start_time + hold_data.duration, + _ => last.start_time, + }; -pub fn get_beatmap_od(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.overall_difficulty) -} + Ok(duration as i32) + } + drain_time: i32 = |b| { + let first = b.hit_objects.first().ok_or_else(|| Error::Other("Empty hitobject list".into()))?; + let last = b.hit_objects.last().ok_or_else(|| Error::Other("Empty hitobject list".into()))?; -pub fn get_beatmap_ar(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.approach_rate) -} + let duration = match &last.kind { + HitObjectKind::Hold(hold_data) => last.start_time + hold_data.duration, + _ => last.start_time, + }; -pub fn get_beatmap_cs(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.circle_size) -} + Ok((duration - first.start_time) as i32) + } + mode: GameMode = |b| { + Ok(GameMode::from(b.mode as u32)) + } -pub fn get_beatmap_hp(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.hp_drain_rate) } -pub fn get_beatmap_object_count(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.hit_objects.len() as u32) -} -pub fn get_beatmap_slider_count(p: &Process, state: &mut State) -> Result { - let path = get_beatmap_path(p, state)?; - let b = RmBeatmap::from_path(path)?; - Ok(b.hit_objects - .iter() - .filter(|h| matches!(h.kind, HitObjectKind::Slider(_))) - .count() as i32) +// cant do this in file mode +pub fn status(p: &Process, state: &mut State) -> Result { + // cant do this in file mode + crate::reader::beatmap::stable::memory::status(p, state) } -pub fn get_beatmap_star_rating(p: &Process, state: &mut State) -> Result { - let folder = get_folder(p, state)?; - let filename = get_filename(p, state)?; +pub fn star_rating(p: &Process, state: &mut State) -> Result { + let folder = folder(p, state)?; + let filename = filename(p, state)?; let songs_path = get_path_folder(p, state)?; let path = songs_path.join(folder).join(filename); let b = rosu_pp::Beatmap::from_path(path)?; @@ -170,16 +133,20 @@ pub fn get_beatmap_star_rating(p: &Process, state: &mut State) -> Result Result { - let beatmap_addr = get_beatmap_path(p, state)?; +pub fn md5(p: &Process, state: &mut State) -> Result { + // TODO: implement this for now will get from memory + crate::reader::beatmap::stable::memory::md5(p, state) +} +pub fn stats(p: &Process, state: &mut State) -> Result { + let beatmap_addr = path(p, state)?; let b = RmBeatmap::from_path(beatmap_addr)?; Ok(BeatmapStats { ar: b.approach_rate, od: b.overall_difficulty, cs: b.circle_size, hp: b.hp_drain_rate, - total_length: get_beatmap_drain_time(p, state)?, - star_rating: get_beatmap_star_rating(p, state)?, + total_length: total_length(p, state)?, + star_rating: star_rating(p, state)?, object_count: b.hit_objects.len() as i32, slider_count: b .hit_objects @@ -189,18 +156,18 @@ pub fn get_beatmap_stats(p: &Process, state: &mut State) -> Result Result { - let beatmap_file = get_beatmap_path(p, state)?; - let beatmap_addr = get_beatmap_addr(p, state)?; +pub fn info(p: &Process, state: &mut State) -> Result { + let beatmap_file = path(p, state)?; + let beatmap_addr = beatmap_addr(p, state)?; let b = RmBeatmap::from_path(beatmap_file)?; // done like that to be more efficient reading the string one by one would need to reload addr everytime which cost more Ok(BeatmapInfo { technical: BeatmapTechnicalInfo { - md5: crate::reader::beatmap::stable::file::get_beatmap_md5(p, state)?, + md5: crate::reader::beatmap::stable::file::md5(p, state)?, id: b.beatmap_id, set_id: b.beatmap_set_id, mode: GameMode::Osu, - ranked_status: crate::reader::beatmap::stable::file::get_beatmap_status(p, state)?, + ranked_status: status(p, state)?, }, metadata: BeatmapMetadata { author: b.artist, @@ -215,8 +182,8 @@ pub fn get_beatmap_info(p: &Process, state: &mut State) -> Result Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.location.folder) -} -pub fn get_filename(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.location.filename) -} - -pub fn get_audio(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.location.audio) -} - -pub fn get_cover(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.location.cover) -} diff --git a/src/reader/beatmap/stable/memory.rs b/src/reader/beatmap/stable/memory.rs index 0d8a33b..111b594 100644 --- a/src/reader/beatmap/stable/memory.rs +++ b/src/reader/beatmap/stable/memory.rs @@ -3,115 +3,58 @@ use crate::reader::beatmap::common::{ BeatmapInfo, BeatmapLocation, BeatmapMetadata, BeatmapStats, BeatmapStatus, BeatmapTechnicalInfo, }; -use crate::reader::beatmap::stable::{ - get_beatmap_addr, offset::BEATMAP_OFFSET, read_from_beatmap_ptr_string, -}; +use crate::reader::beatmap::stable::{beatmap_addr, offset::BEATMAP_OFFSET}; +use crate::reader::helpers::{read_f32, read_i32, read_string, read_u32}; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; - -pub fn get_beatmap_md5(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.technical.md5) -} - -pub fn get_beatmap_id(p: &Process, state: &mut State) -> Result { - Ok(p.read_i32(get_beatmap_addr(p, state)? + BEATMAP_OFFSET.technical.id)?) -} - -pub fn get_beatmap_set_id(p: &Process, state: &mut State) -> Result { - Ok(p.read_i32(get_beatmap_addr(p, state)? + BEATMAP_OFFSET.technical.set_id)?) -} - -pub fn get_beatmap_mode(p: &Process, state: &mut State) -> Result { - Ok(GameMode::from(p.read_i32( - get_beatmap_addr(p, state)? + BEATMAP_OFFSET.technical.mode, - )?)) -} - -pub fn get_beatmap_tags(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.metadata.tags) -} - -pub fn get_beatmap_length(p: &Process, state: &mut State) -> Result { - Ok(p.read_i32(get_beatmap_addr(p, state)? + BEATMAP_OFFSET.stats.total_length)?) -} - -pub fn get_beatmap_drain_time(p: &Process, state: &mut State) -> Result { - Ok(p.read_i32(get_beatmap_addr(p, state)? + BEATMAP_OFFSET.stats.drain_time)?) -} - -pub fn get_beatmap_status(p: &Process, state: &mut State) -> Result { - Ok(BeatmapStatus::from(p.read_i32( - get_beatmap_addr(p, state)? + BEATMAP_OFFSET.technical.ranked_status, - )?)) -} - -pub fn get_author(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.metadata.author) -} - -pub fn get_creator(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.metadata.creator) -} - -pub fn get_title_romanized(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.metadata.title_romanized) -} - -pub fn get_title_original(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.metadata.title_original) -} - -pub fn get_difficulty(p: &Process, state: &mut State) -> Result { - read_from_beatmap_ptr_string(p, state, BEATMAP_OFFSET.metadata.difficulty) -} - -pub fn get_beatmap_od(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; - Ok(p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.od)?) -} - -pub fn get_beatmap_ar(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; - Ok(p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.ar)?) -} - -pub fn get_beatmap_cs(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; - Ok(p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.cs)?) -} - -pub fn get_beatmap_hp(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; - Ok(p.read_f32(beatmap_addr + BEATMAP_OFFSET.stats.hp)?) -} - -pub fn get_beatmap_object_count(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; - Ok(p.read_u32(beatmap_addr + BEATMAP_OFFSET.stats.object_count)?) -} - -pub fn get_beatmap_slider_count(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; - Ok(p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.slider_count)?) -} - -pub fn get_beatmap_stats(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; +use crate::generate_offset_getter; + +generate_offset_getter! { + id: i32 = read_i32(BEATMAP_OFFSET.technical.id, beatmap_addr); + set_id: i32 = read_i32(BEATMAP_OFFSET.technical.set_id, beatmap_addr); + tags: String = read_string(BEATMAP_OFFSET.metadata.tags, beatmap_addr); + length: i32 = read_i32(BEATMAP_OFFSET.stats.total_length, beatmap_addr); + drain_time: i32 = read_i32(BEATMAP_OFFSET.stats.drain_time, beatmap_addr); + author: String = read_string(BEATMAP_OFFSET.metadata.author, beatmap_addr); + creator: String = read_string(BEATMAP_OFFSET.metadata.creator, beatmap_addr); + md5: String = read_string(BEATMAP_OFFSET.technical.md5, beatmap_addr); + title_romanized: String = read_string(BEATMAP_OFFSET.metadata.title_romanized, beatmap_addr); + title: String = read_string(BEATMAP_OFFSET.metadata.title_original, beatmap_addr); + difficulty: String = read_string(BEATMAP_OFFSET.metadata.difficulty, beatmap_addr); + od: f32 = read_f32(BEATMAP_OFFSET.stats.od, beatmap_addr); + ar: f32 = read_f32(BEATMAP_OFFSET.stats.ar, beatmap_addr); + cs: f32 = read_f32(BEATMAP_OFFSET.stats.cs, beatmap_addr); + hp: f32 = read_f32(BEATMAP_OFFSET.stats.hp, beatmap_addr); + object_count: u32 = read_u32(BEATMAP_OFFSET.stats.object_count, beatmap_addr); + slider_count: i32 = read_i32(BEATMAP_OFFSET.stats.slider_count, beatmap_addr); + folder: String = read_string(BEATMAP_OFFSET.location.folder, beatmap_addr); + filename: String = read_string(BEATMAP_OFFSET.location.filename, beatmap_addr); + audio: String = read_string(BEATMAP_OFFSET.location.audio, beatmap_addr); + cover: String = read_string(BEATMAP_OFFSET.location.cover, beatmap_addr); + mode: GameMode = read_i32(BEATMAP_OFFSET.technical.mode, beatmap_addr); + status: BeatmapStatus = read_i32(BEATMAP_OFFSET.technical.ranked_status, beatmap_addr); +} + + + +pub fn stats(p: &Process, state: &mut State) -> Result { + let beatmap_addr = beatmap_addr(p, state)?; + // faster than using read_fn because we dont need to reload addr everytime 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)?, total_length: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.total_length)?, - star_rating: crate::reader::beatmap::stable::file::get_beatmap_star_rating(p, state)?, + 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)?, }) } -pub fn get_beatmap_info(p: &Process, state: &mut State) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; +pub fn info(p: &Process, state: &mut State) -> Result { + 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 Ok(BeatmapInfo { technical: BeatmapTechnicalInfo { @@ -138,7 +81,7 @@ pub fn get_beatmap_info(p: &Process, state: &mut State) -> Result Result { +pub(crate) fn beatmap_addr(p: &Process, state: &mut State) -> Result { match check_game_state(p, state, GameState::SongSelect)? || check_game_state(p, state, GameState::Editor)? || check_game_state(p, state, GameState::Playing)? @@ -20,12 +19,3 @@ pub(crate) fn get_beatmap_addr(p: &Process, state: &mut State) -> Result Err(Error::NotAvailable("Not in song select".to_string())), } } - -pub(crate) fn read_from_beatmap_ptr_string( - p: &Process, - state: &mut State, - offset: i32, -) -> Result { - let beatmap_addr = get_beatmap_addr(p, state)?; - Ok(p.read_string(beatmap_addr + offset)?) -} diff --git a/src/reader/helpers.rs b/src/reader/helpers.rs new file mode 100644 index 0000000..b312a57 --- /dev/null +++ b/src/reader/helpers.rs @@ -0,0 +1,37 @@ +use crate::reader::structs::State; +use crate::Error; +use rosu_mem::process::{Process, ProcessTraits}; + +macro_rules! generate_reader_fn { + ( + $name:ident, $ret_ty:ty, $read_fn:ident + ) => { + pub(crate) fn $name( + p: &Process, + state: &mut State, + offset: i32, + get_base_addr: fn(&Process, &mut State) -> Result, + ) -> Result<$ret_ty, Error> { + let base_addr = get_base_addr(p, state)?; + Ok(p.$read_fn(base_addr + offset)?) + } + }; +} + +generate_reader_fn!(read_string, String, read_string); +generate_reader_fn!(read_i32, i32, read_i32); +generate_reader_fn!(read_f32, f32, read_f32); +generate_reader_fn!(read_u32, u32, read_u32); + +#[macro_export] +macro_rules! generate_offset_getter { + ( + $( $fn_name:ident : $ret_ty:ty = $read_fn:ident ( $offset:expr , $get_base:ident ); )* + ) => { + $( + pub fn $fn_name(p: &Process, state: &mut State) -> Result<$ret_ty, Error> { + Ok(<$ret_ty>::from($read_fn(p, state, $offset, $get_base)?)) + } + )* + }; +} \ No newline at end of file diff --git a/src/reader/mod.rs b/src/reader/mod.rs index 3d7a224..8177b38 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -1,6 +1,7 @@ pub mod beatmap; pub mod common; pub mod gameplay; +pub mod helpers; pub mod resultscreen; pub mod user; From a22a3c73e0b2f6b1f94795dcde8083ef7844aca7 Mon Sep 17 00:00:00 2001 From: Glubus <50545507+Glubus@users.noreply.github.com> Date: Sun, 13 Jul 2025 01:49:04 +0200 Subject: [PATCH 2/6] feat: passed common in macro --- Cargo.toml | 6 ++++- examples/{getig.rs => get_time.rs} | 4 ++-- examples/pp.rs | 8 +++---- src/reader/beatmap/stable/file.rs | 8 +++---- src/reader/common/mod.rs | 12 +++++----- src/reader/common/stable/memory.rs | 35 +++++++++++++++++----------- src/reader/gameplay/mod.rs | 4 ++-- src/reader/gameplay/stable/memory.rs | 7 +++--- src/reader/mod.rs | 4 ++-- 9 files changed, 50 insertions(+), 38 deletions(-) rename examples/{getig.rs => get_time.rs} (77%) diff --git a/Cargo.toml b/Cargo.toml index bffbf6b..ea63b56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,10 @@ rosu-mem = "2.0.0" rosu-map = "0.2.1" rosu-pp = "3.1.0" rayon = { version = "1.10.0", optional = true } +rosu-mods = "0.3.1" [dev-dependencies] -rosu-mods = "0.3.1" \ No newline at end of file +rosu-mods = "0.3.1" + +[profile.dev.package.rosu-mem] +opt-level = 3 \ No newline at end of file diff --git a/examples/getig.rs b/examples/get_time.rs similarity index 77% rename from examples/getig.rs rename to examples/get_time.rs index f139c30..2f84277 100644 --- a/examples/getig.rs +++ b/examples/get_time.rs @@ -1,12 +1,12 @@ use rosu_memory_lib::init_loop; -use rosu_memory_lib::reader::gameplay::stable::memory::get_ig_time; +use rosu_memory_lib::reader::common::stable::memory::game_time; use rosu_memory_lib::Error; fn main() -> Result<(), Error> { let (mut state, process) = init_loop(500)?; println!("Successfully initialized!"); loop { - match get_ig_time(&process, &mut state) { + match game_time(&process, &mut state) { Ok(ig_time) => println!("Current ig time: {ig_time:?}"), Err(e) => println!("Error: {e:?}"), } diff --git a/examples/pp.rs b/examples/pp.rs index 65835aa..2b7df85 100644 --- a/examples/pp.rs +++ b/examples/pp.rs @@ -1,7 +1,7 @@ use rosu_mem::process::Process; use rosu_memory_lib::init_loop; use rosu_memory_lib::reader::beatmap::stable::file::path; -use rosu_memory_lib::reader::common::stable::memory::get_menu_mods; +use rosu_memory_lib::reader::common::stable::memory::menu_game_mode; use rosu_memory_lib::reader::structs::State; use rosu_memory_lib::Error; use rosu_mods::GameModsLegacy; @@ -102,9 +102,9 @@ fn process_game_state( match path(process, state) { Ok(beatmap_path) => { // Update mods if they changed - if let Ok(new_mods) = get_menu_mods(process, state) { - mods_changed = calc_state.update_mods(new_mods); - } + if let Ok(new_mods) = menu_game_mode(process, state) { + mods_changed = calc_state.update_mods(new_mods as i32); + } // Update beatmap if path changed and mods changed else it's useless to recalculate if calc_state.update_beatmap(beatmap_path)? && mods_changed { diff --git a/src/reader/beatmap/stable/file.rs b/src/reader/beatmap/stable/file.rs index aeae25e..4b289c1 100644 --- a/src/reader/beatmap/stable/file.rs +++ b/src/reader/beatmap/stable/file.rs @@ -7,7 +7,7 @@ use crate::reader::beatmap::common::{ }; use crate::reader::beatmap::stable::memory::{audio, filename, folder}; use crate::reader::beatmap::stable::{beatmap_addr, offset::BEATMAP_OFFSET}; -use crate::reader::common::stable::memory::get_path_folder; +use crate::reader::common::stable::memory::path_folder; use crate::reader::structs::State; use crate::Error; use rosu_map::section::hit_objects::HitObjectKind; @@ -17,7 +17,7 @@ use rosu_mem::process::{Process, ProcessTraits}; pub fn path(p: &Process, state: &mut State) -> Result { let folder = folder(p, state)?; let filename = filename(p, state)?; - let songs_path = get_path_folder(p, state)?; + let songs_path = path_folder(p, state)?; Ok(songs_path.join(folder).join(filename)) } @@ -25,7 +25,7 @@ pub fn path(p: &Process, state: &mut State) -> Result { pub fn audio_path(p: &Process, state: &mut State) -> Result { let folder = folder(p, state)?; let audio = audio(p, state)?; - let songs_path = get_path_folder(p, state)?; + let songs_path = path_folder(p, state)?; Ok(songs_path.join(folder).join(audio)) } @@ -121,7 +121,7 @@ pub fn status(p: &Process, state: &mut State) -> Result { pub fn star_rating(p: &Process, state: &mut State) -> Result { let folder = folder(p, state)?; let filename = filename(p, state)?; - let songs_path = get_path_folder(p, state)?; + let songs_path = path_folder(p, state)?; let path = songs_path.join(folder).join(filename); let b = rosu_pp::Beatmap::from_path(path)?; let diff_attrs = rosu_pp::Difficulty::new().calculate(&b); diff --git a/src/reader/common/mod.rs b/src/reader/common/mod.rs index aeecdab..8401b8a 100644 --- a/src/reader/common/mod.rs +++ b/src/reader/common/mod.rs @@ -120,27 +120,27 @@ impl<'a> CommonReader<'a> { } } - pub fn get_game_state(&mut self) -> Result { + pub fn game_state(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_game_state(self.process, self.state), + OsuClientKind::Stable => stable::memory::game_state(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_menu_mods(&mut self) -> Result { + pub fn menu_game_mode(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_menu_mods(self.process, self.state), + OsuClientKind::Stable => stable::memory::menu_game_mode(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_path_folder(&mut self) -> Result { + pub fn path_folder(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_path_folder(self.process, self.state), + OsuClientKind::Stable => stable::memory::path_folder(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), diff --git a/src/reader/common/stable/memory.rs b/src/reader/common/stable/memory.rs index 021d077..fee7293 100644 --- a/src/reader/common/stable/memory.rs +++ b/src/reader/common/stable/memory.rs @@ -1,26 +1,24 @@ use std::path::PathBuf; use crate::reader::common::stable::offset::COMMON_OFFSET; -use crate::reader::common::GameState; +use crate::reader::common::{GameMode, GameState}; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; +use crate::generate_offset_getter; +use crate::reader::helpers::{read_i32, read_u32}; -pub fn get_game_state(p: &Process, state: &mut State) -> Result { - let status_ptr = p.read_i32(state.addresses.status - COMMON_OFFSET.status)?; - Ok(GameState::from(p.read_u32(status_ptr)?)) +pub fn status_addr(p: &Process, state: &mut State) -> Result { + Ok(p.read_i32(state.addresses.status - COMMON_OFFSET.status)?) } -pub fn check_game_state(p: &Process, state: &mut State, g_state: GameState) -> Result { - Ok(get_game_state(p, state)? == g_state) -} /// Returns a path to the `Songs` folder /// /// **Platform-specific** /// - Windows: Will return full absolute path to the `Songs` folder /// - Linux: Might return relative path, carefully check by yourself -pub(crate) fn get_path_folder(p: &Process, state: &mut State) -> Result { +pub(crate) fn path_folder(p: &Process, state: &mut State) -> Result { let settings_ptr = p.read_i32(state.addresses.settings + COMMON_OFFSET.settings_ptr)?; let settings_addr = p.read_i32(settings_ptr + COMMON_OFFSET.settings_addr)?; let path = p.read_string(settings_addr + COMMON_OFFSET.path)?; @@ -37,12 +35,21 @@ pub(crate) fn get_path_folder(p: &Process, state: &mut State) -> Result Result { - let menu_mods_ptr = p.read_i32(state.addresses.menu_mods + COMMON_OFFSET.mods_ptr)?; - Ok(p.read_i32(menu_mods_ptr)?) +pub fn menu_mods_addr(p: &Process, state: &mut State) -> Result { + Ok(p.read_i32(state.addresses.menu_mods + COMMON_OFFSET.mods_ptr)?) +} + +pub fn playtime_addr(p: &Process, state: &mut State) -> Result { + Ok(p.read_i32(state.addresses.playtime + COMMON_OFFSET.ig_time)?) } -pub fn get_ig_time(p: &Process, state: &mut State) -> Result { - let playtime_ptr = p.read_i32(state.addresses.playtime + COMMON_OFFSET.ig_time)?; - Ok(p.read_i32(playtime_ptr)?) +generate_offset_getter! { + game_state: GameState = read_u32(COMMON_OFFSET.status, status_addr); + menu_game_mode: GameMode = read_i32(COMMON_OFFSET.mods_ptr, menu_mods_addr); + game_time: i32 = read_i32(0, playtime_addr); +} + +// this is an helper function to be faster for anyone +pub fn check_game_state(p: &Process, state: &mut State, g_state: GameState) -> Result { + Ok(game_state(p, state)? == g_state) } diff --git a/src/reader/gameplay/mod.rs b/src/reader/gameplay/mod.rs index 391ef0c..e798d51 100644 --- a/src/reader/gameplay/mod.rs +++ b/src/reader/gameplay/mod.rs @@ -76,9 +76,9 @@ impl<'a> GameplayReader<'a> { } } - pub fn get_ig_time(&mut self) -> Result { + pub fn game_time(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_ig_time(self.process, self.state), + OsuClientKind::Stable => crate::reader::common::stable::memory::game_time(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), diff --git a/src/reader/gameplay/stable/memory.rs b/src/reader/gameplay/stable/memory.rs index e9acbab..d2e8080 100644 --- a/src/reader/gameplay/stable/memory.rs +++ b/src/reader/gameplay/stable/memory.rs @@ -1,4 +1,4 @@ -use crate::reader::common::stable::memory::check_game_state; +use crate::reader::common::stable::memory::{check_game_state, game_time}; use crate::reader::common::GameState; use crate::reader::gameplay::common::GameplayInfo; use crate::reader::gameplay::stable::offset::GAMEPLAY_OFFSET; @@ -77,8 +77,9 @@ pub fn get_username(p: &Process, state: &mut State) -> Result { Ok(username) } + pub fn get_ig_time(p: &Process, state: &mut State) -> Result { - crate::reader::common::stable::memory::get_ig_time(p, state) + crate::reader::common::stable::memory::game_time(p, state) } pub fn get_retries(p: &Process, state: &mut State) -> Result { @@ -149,7 +150,7 @@ pub fn get_gameplay_info(p: &Process, state: &mut State) -> Result Result<(), Error>, { loop { - if get_game_state(p, state)? == g_state { + if game_state(p, state)? == g_state { return Ok(()); } if let Some(f) = &callback { From d3bc6edf592444cebc39f7a1394c33a99a99dbd7 Mon Sep 17 00:00:00 2001 From: Glubus <50545507+Glubus@users.noreply.github.com> Date: Sun, 13 Jul 2025 02:08:29 +0200 Subject: [PATCH 3/6] feat: gameplay now using macro too --- src/reader/gameplay/mod.rs | 60 ++++----- src/reader/gameplay/stable/memory.rs | 176 +++++++++------------------ src/reader/gameplay/stable/offset.rs | 4 +- src/reader/helpers.rs | 7 +- 4 files changed, 92 insertions(+), 155 deletions(-) diff --git a/src/reader/gameplay/mod.rs b/src/reader/gameplay/mod.rs index e798d51..563c15e 100644 --- a/src/reader/gameplay/mod.rs +++ b/src/reader/gameplay/mod.rs @@ -22,54 +22,54 @@ impl<'a> GameplayReader<'a> { } } - pub fn get_score_gameplay(&mut self) -> Result { + pub fn score(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_score_gameplay(self.process, self.state), + OsuClientKind::Stable => stable::memory::score(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_mods(&mut self) -> Result { + pub fn mods(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_mods(self.process, self.state), + OsuClientKind::Stable => stable::memory::mods(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_combo(&mut self) -> Result { + pub fn combo(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_combo(self.process, self.state), + OsuClientKind::Stable => stable::memory::combo(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_max_combo(&mut self) -> Result { + pub fn max_combo(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_max_combo(self.process, self.state), + OsuClientKind::Stable => stable::memory::max_combo(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_current_hp(&mut self) -> Result { + pub fn hp(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_current_hp(self.process, self.state), + OsuClientKind::Stable => stable::memory::hp(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_username(&mut self) -> Result { + pub fn username(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_username(self.process, self.state), + OsuClientKind::Stable => stable::memory::username(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), @@ -85,81 +85,81 @@ impl<'a> GameplayReader<'a> { } } - pub fn get_retries(&mut self) -> Result { + pub fn retries(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_retries(self.process, self.state), + OsuClientKind::Stable => stable::memory::retries(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_hits_300(&mut self) -> Result { + pub fn hits_300(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_hits_300(self.process, self.state), + OsuClientKind::Stable => stable::memory::hits_300(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_hits_100(&mut self) -> Result { + pub fn hits_100(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_hits_100(self.process, self.state), + OsuClientKind::Stable => stable::memory::hits_100(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_hits_50(&mut self) -> Result { + pub fn hits_50(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_hits_50(self.process, self.state), + OsuClientKind::Stable => stable::memory::hits_50(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_hits_miss(&mut self) -> Result { + pub fn hits_miss(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_hits_miss(self.process, self.state), + OsuClientKind::Stable => stable::memory::hits_miss(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_hits_geki(&mut self) -> Result { + pub fn hits_geki(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_hits_geki(self.process, self.state), + OsuClientKind::Stable => stable::memory::hits_geki(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_hits_katu(&mut self) -> Result { + pub fn hits_katu(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_hits_katu(self.process, self.state), + OsuClientKind::Stable => stable::memory::hits_katu(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_hits(&mut self) -> Result { + pub fn hits(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_hits(self.process, self.state), + OsuClientKind::Stable => stable::memory::hits(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), } } - pub fn get_gameplay_info(&mut self) -> Result { + pub fn gameplay_info(&mut self) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::get_gameplay_info(self.process, self.state), + OsuClientKind::Stable => stable::memory::info(self.process, self.state), _ => Err(Error::Unsupported( "Unsupported osu type for now".to_string(), )), diff --git a/src/reader/gameplay/stable/memory.rs b/src/reader/gameplay/stable/memory.rs index d2e8080..d851f47 100644 --- a/src/reader/gameplay/stable/memory.rs +++ b/src/reader/gameplay/stable/memory.rs @@ -1,4 +1,4 @@ -use crate::reader::common::stable::memory::{check_game_state, game_time}; +use crate::reader::common::stable::memory::{check_game_state}; use crate::reader::common::GameState; use crate::reader::gameplay::common::GameplayInfo; use crate::reader::gameplay::stable::offset::GAMEPLAY_OFFSET; @@ -6,159 +6,95 @@ use crate::reader::structs::Hit; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; +use crate::{generate_offset_getter, reader::helpers::{read_f64, read_i16, read_i32, read_string, read_u64}}; -pub fn get_base(p: &Process, state: &mut State) -> Result { +pub fn rulesets_addr(p: &Process, state: &mut State) -> Result { if check_game_state(p, state, GameState::Playing)? { - let rulesets = match p.read_i32(state.addresses.rulesets - GAMEPLAY_OFFSET.ptr) { - Ok(val) => val, - Err(_) => return Err(Error::NotAvailable("Still loading".to_string())), - }; - - let ruleset_addr = match p.read_i32(rulesets + GAMEPLAY_OFFSET.addr) { - Ok(val) => val, - Err(_) => return Err(Error::NotAvailable("Still loading".to_string())), - }; - - let gameplay_base = match p.read_i32(ruleset_addr + GAMEPLAY_OFFSET.base) { - Ok(val) => val, - Err(_) => return Err(Error::NotAvailable("Still loading".to_string())), - }; - - Ok(gameplay_base) + Ok(p.read_i32(state.addresses.rulesets - GAMEPLAY_OFFSET.ptr)?) } else { Err(Error::NotAvailable("Not in Playing".to_string())) } } -pub fn get_base2(p: &Process, state: &mut State) -> Result { - let gameplay_base = get_base(p, state)?; - match p.read_i32(gameplay_base + GAMEPLAY_OFFSET.base2) { - Ok(val) => Ok(val), - Err(_) => Err(Error::NotAvailable("Still loading".to_string())), - } -} - -pub fn get_score_gameplay(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let score = p.read_i32(base2 + GAMEPLAY_OFFSET.score)?; - Ok(score) -} - -pub fn get_mods(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let mods_xor_base = p.read_i32(base2 + GAMEPLAY_OFFSET.mods)?; - let mods_xor1 = p.read_u64(mods_xor_base + GAMEPLAY_OFFSET.mods_xor)?; - let mods_xor2 = p.read_u64(mods_xor_base + GAMEPLAY_OFFSET.mods_xor2)?; +pub fn mods(p: &Process, state: &mut State) -> Result { + let mods_xor1 = mods_xor1(p, state)?; + let mods_xor2 = mods_xor2(p, state)?; Ok((mods_xor1 ^ mods_xor2) as u32) } -pub fn get_combo(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let combo = p.read_i16(base2 + GAMEPLAY_OFFSET.combo)?; - Ok(combo) -} - -pub fn get_max_combo(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let max_combo = p.read_i16(base2 + GAMEPLAY_OFFSET.max_combo)?; - Ok(max_combo) +generate_offset_getter! { + ruleset_addr: i32 = read_i32(GAMEPLAY_OFFSET.ptr, rulesets_addr); + gameplay_base: i32 = read_i32(GAMEPLAY_OFFSET.base, ruleset_addr); + score_base: i32 = read_i32(GAMEPLAY_OFFSET.score_base, gameplay_base); + hp_base: i32 = read_i32(GAMEPLAY_OFFSET.hp_base, gameplay_base); + score: i32 = read_i32(GAMEPLAY_OFFSET.score, score_base); + mods_xor_base: i32 = read_i32(GAMEPLAY_OFFSET.mods, gameplay_base); + mods_xor1: u64 = read_u64(GAMEPLAY_OFFSET.mods_xor, mods_xor_base); + mods_xor2: u64 = read_u64(GAMEPLAY_OFFSET.mods_xor2, mods_xor_base); + combo: i16 = read_i16(GAMEPLAY_OFFSET.combo, score_base); + max_combo: i16 = read_i16(GAMEPLAY_OFFSET.max_combo, score_base); + hp: f64 = read_f64(GAMEPLAY_OFFSET.hp, hp_base); + username: String = read_string(GAMEPLAY_OFFSET.username, score_base); + hits_300: i16 = read_i16(GAMEPLAY_OFFSET.hits._300, score_base); + hits_100: i16 = read_i16(GAMEPLAY_OFFSET.hits._100, score_base); + hits_50: i16 = read_i16(GAMEPLAY_OFFSET.hits._50, score_base); + hits_miss: i16 = read_i16(GAMEPLAY_OFFSET.hits._miss, score_base); + hits_geki: i16 = read_i16(GAMEPLAY_OFFSET.hits._geki, score_base); + hits_katu: i16 = read_i16(GAMEPLAY_OFFSET.hits._katu, score_base); } -pub fn get_current_hp(p: &Process, state: &mut State) -> Result { - let base = get_base(p, state)?; - let hp_base = p.read_i32(base + GAMEPLAY_OFFSET.hp_base)?; - let hp = p.read_f64(hp_base + GAMEPLAY_OFFSET.hp)?; - Ok(hp) -} -pub fn get_username(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let username = p.read_string(base2 + GAMEPLAY_OFFSET.username)?; - Ok(username) -} -pub fn get_ig_time(p: &Process, state: &mut State) -> Result { +/// this is a wrapper to not confuse people it could be deleted in the future +/// use -> crate::reader::common::stable::memory::game_time +pub fn game_time(p: &Process, state: &mut State) -> Result { crate::reader::common::stable::memory::game_time(p, state) } -pub fn get_retries(p: &Process, state: &mut State) -> Result { +pub fn retries(p: &Process, state: &mut State) -> Result { let igt_addr = p.read_i32(state.addresses.base - GAMEPLAY_OFFSET.ruleset)?; let retries = p.read_i32(igt_addr + GAMEPLAY_OFFSET.retries)?; Ok(retries) } -pub fn get_hits_300(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let hits_300 = p.read_i16(base2 + GAMEPLAY_OFFSET.hits._300)?; - Ok(hits_300) -} - -pub fn get_hits_100(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let hits_100 = p.read_i16(base2 + GAMEPLAY_OFFSET.hits._100)?; - Ok(hits_100) -} - -pub fn get_hits_50(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let hits_50 = p.read_i16(base2 + GAMEPLAY_OFFSET.hits._50)?; - Ok(hits_50) -} - -pub fn get_hits_miss(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let hits_miss = p.read_i16(base2 + GAMEPLAY_OFFSET.hits._miss)?; - Ok(hits_miss) -} - -pub fn get_hits_geki(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let hits_geki = p.read_i16(base2 + GAMEPLAY_OFFSET.hits._geki)?; - Ok(hits_geki) -} - -pub fn get_hits_katu(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; - let hits_katu = p.read_i16(base2 + GAMEPLAY_OFFSET.hits._katu)?; - Ok(hits_katu) -} - -pub fn get_hits(p: &Process, state: &mut State) -> Result { - let base2 = get_base2(p, state)?; +pub fn hits(p: &Process, state: &mut State) -> Result { + let score_base = score_base(p, state)?; + // TODO: check issue for reading the full block and + // separating bits Ok(Hit { - _300: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._300)?, - _100: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._100)?, - _50: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._50)?, - _miss: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._miss)?, - _geki: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._geki)?, - _katu: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._katu)?, + _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)?, }) } -pub fn get_gameplay_info(p: &Process, state: &mut State) -> Result { - let base = get_base(p, state)?; - let base2 = get_base2(p, state)?; +pub fn info(p: &Process, state: &mut State) -> Result { + let gameplay_base = gameplay_base(p, state)?; + let score_base = score_base(p, state)?; - let hp = p.read_f64(p.read_i32(base + GAMEPLAY_OFFSET.hp_base)? + GAMEPLAY_OFFSET.hp)?; - let mods = get_mods(p, state)?; + let hp = p.read_f64(p.read_i32(gameplay_base + GAMEPLAY_OFFSET.hp_base)? + GAMEPLAY_OFFSET.hp)?; + let mods = mods(p, state)?; Ok(GameplayInfo { - score: p.read_i32(base2 + GAMEPLAY_OFFSET.score)?, + score: p.read_i32(score_base + GAMEPLAY_OFFSET.score)?, mods, - combo: p.read_i16(base2 + GAMEPLAY_OFFSET.combo)?, - max_combo: p.read_i16(base2 + GAMEPLAY_OFFSET.max_combo)?, + combo: p.read_i16(score_base + GAMEPLAY_OFFSET.combo)?, + max_combo: p.read_i16(score_base + GAMEPLAY_OFFSET.max_combo)?, hp, - username: p.read_string(base2 + GAMEPLAY_OFFSET.username)?, + username: p.read_string(score_base + GAMEPLAY_OFFSET.username)?, ig_time: game_time(p, state)?, // different base - retries: get_retries(p, state)?, // different base + retries: retries(p, state)?, // different base hits: Hit { - _300: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._300)?, - _100: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._100)?, - _50: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._50)?, - _miss: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._miss)?, - _geki: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._geki)?, - _katu: p.read_i16(base2 + GAMEPLAY_OFFSET.hits._katu)?, + _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)?, }, }) } diff --git a/src/reader/gameplay/stable/offset.rs b/src/reader/gameplay/stable/offset.rs index 719fc47..4530d7a 100644 --- a/src/reader/gameplay/stable/offset.rs +++ b/src/reader/gameplay/stable/offset.rs @@ -2,7 +2,7 @@ pub struct GameplayOffset { pub ptr: i32, pub addr: i32, pub base: i32, - pub base2: i32, + pub score_base: i32, pub ruleset: i32, pub hp_base: i32, pub score: i32, @@ -21,7 +21,7 @@ pub const GAMEPLAY_OFFSET: GameplayOffset = GameplayOffset { ptr: 0xb, addr: 0x4, base: 0x68, - base2: 0x38, + score_base: 0x38, ruleset: 0x33, hp_base: 0x40, score: 0x78, diff --git a/src/reader/helpers.rs b/src/reader/helpers.rs index b312a57..b945bc1 100644 --- a/src/reader/helpers.rs +++ b/src/reader/helpers.rs @@ -17,12 +17,13 @@ macro_rules! generate_reader_fn { } }; } - generate_reader_fn!(read_string, String, read_string); +generate_reader_fn!(read_i16, i16, read_i16); generate_reader_fn!(read_i32, i32, read_i32); -generate_reader_fn!(read_f32, f32, read_f32); generate_reader_fn!(read_u32, u32, read_u32); - +generate_reader_fn!(read_u64, u64, read_u64); +generate_reader_fn!(read_f32, f32, read_f32); +generate_reader_fn!(read_f64, f64, read_f64); #[macro_export] macro_rules! generate_offset_getter { ( From 4cd0b88270ec0f61e1e2bd0a62db8752c884ddf5 Mon Sep 17 00:00:00 2001 From: Glubus <50545507+Glubus@users.noreply.github.com> Date: Sun, 13 Jul 2025 02:21:07 +0200 Subject: [PATCH 4/6] fix: bug from me :) --- examples/gameplay.rs | 76 ++++++++++++++++++++++++++++ src/reader/common/stable/memory.rs | 2 +- src/reader/gameplay/stable/memory.rs | 7 ++- 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 examples/gameplay.rs diff --git a/examples/gameplay.rs b/examples/gameplay.rs new file mode 100644 index 0000000..47a3b68 --- /dev/null +++ b/examples/gameplay.rs @@ -0,0 +1,76 @@ +use rosu_memory_lib::init_loop; +use rosu_memory_lib::reader::gameplay::GameplayReader; +use rosu_memory_lib::reader::common::OsuClientKind; +use rosu_memory_lib::Error; + +fn main() -> Result<(), Error> { + let (mut state, process) = init_loop(500)?; + let mut gameplay_reader = GameplayReader::new(&process, &mut state, OsuClientKind::Stable); + loop { + match gameplay_reader.combo() { + Ok(combo) => println!("Current combo: {combo}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hp() { + Ok(hp) => println!("Current hp: {hp}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.username() { + Ok(username) => println!("Current username: {username}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hits() { + Ok(hits) => println!("Current hits: {hits:?}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.game_time() { + Ok(game_time) => println!("Current game time: {game_time}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.retries() { + Ok(retries) => println!("Current retries: {retries}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hits_300() { + Ok(hits_300) => println!("Current hits 300: {hits_300}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hits_100() { + Ok(hits_100) => println!("Current hits 100: {hits_100}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hits_50() { + Ok(hits_50) => println!("Current hits 50: {hits_50}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hits_miss() { + Ok(hits_miss) => println!("Current hits miss: {hits_miss}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hits_geki() { + Ok(hits_geki) => println!("Current hits geki: {hits_geki}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.hits_katu() { + Ok(hits_katu) => println!("Current hits katu: {hits_katu}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.mods() { + Ok(mods) => println!("Current mods: {mods}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.score() { + Ok(score) => println!("Current score: {score}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.max_combo() { + Ok(max_combo) => println!("Current max combo: {max_combo}"), + Err(e) => println!("Error: {e:?}"), + } + match gameplay_reader.gameplay_info() { + Ok(gameplay_info) => println!("Current gameplay info: {gameplay_info:?}"), + Err(e) => println!("Error: {e:?}"), + } + std::thread::sleep(std::time::Duration::from_millis(1000)); + } +} diff --git a/src/reader/common/stable/memory.rs b/src/reader/common/stable/memory.rs index fee7293..0933955 100644 --- a/src/reader/common/stable/memory.rs +++ b/src/reader/common/stable/memory.rs @@ -44,7 +44,7 @@ pub fn playtime_addr(p: &Process, state: &mut State) -> Result { } generate_offset_getter! { - game_state: GameState = read_u32(COMMON_OFFSET.status, status_addr); + game_state: GameState = read_u32(0, status_addr); menu_game_mode: GameMode = read_i32(COMMON_OFFSET.mods_ptr, menu_mods_addr); game_time: i32 = read_i32(0, playtime_addr); } diff --git a/src/reader/gameplay/stable/memory.rs b/src/reader/gameplay/stable/memory.rs index d851f47..1c6c090 100644 --- a/src/reader/gameplay/stable/memory.rs +++ b/src/reader/gameplay/stable/memory.rs @@ -23,12 +23,12 @@ pub fn mods(p: &Process, state: &mut State) -> Result { } generate_offset_getter! { - ruleset_addr: i32 = read_i32(GAMEPLAY_OFFSET.ptr, rulesets_addr); + ruleset_addr: i32 = read_i32(GAMEPLAY_OFFSET.addr, rulesets_addr); gameplay_base: i32 = read_i32(GAMEPLAY_OFFSET.base, ruleset_addr); score_base: i32 = read_i32(GAMEPLAY_OFFSET.score_base, gameplay_base); hp_base: i32 = read_i32(GAMEPLAY_OFFSET.hp_base, gameplay_base); score: i32 = read_i32(GAMEPLAY_OFFSET.score, score_base); - mods_xor_base: i32 = read_i32(GAMEPLAY_OFFSET.mods, gameplay_base); + mods_xor_base: i32 = read_i32(GAMEPLAY_OFFSET.mods, score_base); mods_xor1: u64 = read_u64(GAMEPLAY_OFFSET.mods_xor, mods_xor_base); mods_xor2: u64 = read_u64(GAMEPLAY_OFFSET.mods_xor2, mods_xor_base); combo: i16 = read_i16(GAMEPLAY_OFFSET.combo, score_base); @@ -73,10 +73,9 @@ pub fn hits(p: &Process, state: &mut State) -> Result { } pub fn info(p: &Process, state: &mut State) -> Result { - let gameplay_base = gameplay_base(p, state)?; let score_base = score_base(p, state)?; - let hp = p.read_f64(p.read_i32(gameplay_base + GAMEPLAY_OFFSET.hp_base)? + GAMEPLAY_OFFSET.hp)?; + let hp = hp(p, state)?; let mods = mods(p, state)?; Ok(GameplayInfo { From 97a69fb691b6d1166b8ffeaef57f5a75e274faa8 Mon Sep 17 00:00:00 2001 From: Glubus <50545507+Glubus@users.noreply.github.com> Date: Sun, 13 Jul 2025 12:41:56 +0200 Subject: [PATCH 5/6] refactor & fix: passed everything in macros, added a new macro for reader won 1K line of code wow --- examples/gameplay.rs | 2 +- examples/pp.rs | 5 +- src/reader/beatmap/common.rs | 2 +- src/reader/beatmap/mod.rs | 287 +++-------------------- src/reader/beatmap/stable/file.rs | 6 +- src/reader/beatmap/stable/memory.rs | 4 +- src/reader/common/mod.rs | 38 +-- src/reader/common/stable/memory.rs | 4 +- src/reader/gameplay/mod.rs | 160 ++----------- src/reader/helpers.rs | 66 ++++++ src/reader/resultscreen/common.rs | 2 +- src/reader/resultscreen/mod.rs | 134 ++--------- src/reader/resultscreen/stable/memory.rs | 122 +++------- src/reader/resultscreen/stable/offset.rs | 4 +- 14 files changed, 183 insertions(+), 653 deletions(-) diff --git a/examples/gameplay.rs b/examples/gameplay.rs index 47a3b68..73798bd 100644 --- a/examples/gameplay.rs +++ b/examples/gameplay.rs @@ -67,7 +67,7 @@ fn main() -> Result<(), Error> { Ok(max_combo) => println!("Current max combo: {max_combo}"), Err(e) => println!("Error: {e:?}"), } - match gameplay_reader.gameplay_info() { + match gameplay_reader.info() { Ok(gameplay_info) => println!("Current gameplay info: {gameplay_info:?}"), Err(e) => println!("Error: {e:?}"), } diff --git a/examples/pp.rs b/examples/pp.rs index 2b7df85..d711549 100644 --- a/examples/pp.rs +++ b/examples/pp.rs @@ -102,12 +102,15 @@ fn process_game_state( match path(process, state) { Ok(beatmap_path) => { // Update mods if they changed + println!("Menu game mode: {}", menu_game_mode(process, state)?); if let Ok(new_mods) = menu_game_mode(process, state) { mods_changed = calc_state.update_mods(new_mods as i32); } // Update beatmap if path changed and mods changed else it's useless to recalculate - if calc_state.update_beatmap(beatmap_path)? && mods_changed { + let beatmap_updated = calc_state.update_beatmap(beatmap_path)?; + + if beatmap_updated || mods_changed { calc_state.update_pp(); } } diff --git a/src/reader/beatmap/common.rs b/src/reader/beatmap/common.rs index aa430a1..930ce62 100644 --- a/src/reader/beatmap/common.rs +++ b/src/reader/beatmap/common.rs @@ -58,7 +58,7 @@ pub struct BeatmapStats { pub od: f32, pub cs: f32, pub hp: f32, - pub total_length: i32, + pub length: i32, pub star_rating: BeatmapStarRating, pub object_count: i32, pub slider_count: i32, diff --git a/src/reader/beatmap/mod.rs b/src/reader/beatmap/mod.rs index 346b3d3..917c596 100644 --- a/src/reader/beatmap/mod.rs +++ b/src/reader/beatmap/mod.rs @@ -12,6 +12,7 @@ use crate::reader::common::OsuClientKind; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::Process; +use crate::impl_osu_accessor; pub struct BeatmapReader<'a> { pub process: &'a Process, @@ -32,262 +33,34 @@ impl<'a> BeatmapReader<'a> { }) } - pub fn info(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::info(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - pub fn cover(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::cover(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn path(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::file::path(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn folder(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::folder(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn filename(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::filename(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn audio(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::audio(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn audio_path(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::file::audio_path(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn md5(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::md5(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn id(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::id(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn set_id(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::set_id(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn mode(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::mode(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn tags(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::tags(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn length(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::length(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn drain_time(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => { - stable::memory::drain_time(self.process, self.state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn status(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::status(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn author(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::author(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn creator(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::creator(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn title_romanized(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::title_romanized(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn title(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::title(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn difficulty(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::difficulty(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn od(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::od(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn ar(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::ar(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn cs(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::cs(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hp(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hp(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn object_count(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => { - stable::memory::object_count(self.process, self.state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn slider_count(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => { - stable::memory::slider_count(self.process, self.state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn star_rating(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => { - stable::file::star_rating(self.process, self.state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn stats(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::stats(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } + impl_osu_accessor! { + fn id() -> i32 => stable::memory::id, + fn set_id() -> i32 => stable::memory::set_id, + fn tags() -> String => stable::memory::tags, + fn length() -> i32 => stable::memory::length, + fn drain_time() -> i32 => stable::memory::drain_time, + fn author() -> String => stable::memory::author, + fn creator() -> String => stable::memory::creator, + fn md5() -> String => stable::memory::md5, + fn title_romanized() -> String => stable::memory::title_romanized, + fn title() -> String => stable::memory::title, + fn difficulty() -> String => stable::memory::difficulty, + fn od() -> f32 => stable::memory::od, + fn ar() -> f32 => stable::memory::ar, + fn cs() -> f32 => stable::memory::cs, + fn hp() -> f32 => stable::memory::hp, + fn object_count() -> u32 => stable::memory::object_count, + fn slider_count() -> i32 => stable::memory::slider_count, + fn folder() -> String => stable::memory::folder, + fn filename() -> String => stable::memory::filename, + fn audio() -> String => stable::memory::audio, + fn cover() -> String => stable::memory::cover, + fn mode() -> GameMode => stable::memory::mode, + fn status() -> BeatmapStatus => stable::memory::status, + fn info() -> BeatmapInfo => stable::memory::info, + fn stats() -> BeatmapStats => stable::memory::stats, + fn path() -> PathBuf => stable::file::path, + fn audio_path() -> PathBuf => stable::file::audio_path, + fn star_rating() -> BeatmapStarRating => stable::file::star_rating, } } diff --git a/src/reader/beatmap/stable/file.rs b/src/reader/beatmap/stable/file.rs index 4b289c1..5199c9b 100644 --- a/src/reader/beatmap/stable/file.rs +++ b/src/reader/beatmap/stable/file.rs @@ -84,7 +84,7 @@ generate_beatmap_custom_getter_safe! { object_count: u32 = |b| { Ok(b.hit_objects.len() as u32) } - total_length: i32 = |b| { + length: i32 = |b| { let last = b.hit_objects.last().ok_or_else(|| Error::Other("Empty hitobject list".into()))?; let duration = match &last.kind { @@ -145,7 +145,7 @@ pub fn stats(p: &Process, state: &mut State) -> Result { od: b.overall_difficulty, cs: b.circle_size, hp: b.hp_drain_rate, - total_length: total_length(p, state)?, + length: length(p, state)?, star_rating: star_rating(p, state)?, object_count: b.hit_objects.len() as i32, slider_count: b @@ -182,7 +182,7 @@ pub fn info(p: &Process, state: &mut State) -> Result { od: b.overall_difficulty, cs: b.circle_size, hp: b.hp_drain_rate, - total_length: drain_time(p, state)?, + length: length(p, state)?, star_rating: star_rating(p, state)?, object_count: b.hit_objects.len() as i32, slider_count: b diff --git a/src/reader/beatmap/stable/memory.rs b/src/reader/beatmap/stable/memory.rs index 111b594..1557943 100644 --- a/src/reader/beatmap/stable/memory.rs +++ b/src/reader/beatmap/stable/memory.rs @@ -46,7 +46,7 @@ pub fn stats(p: &Process, state: &mut State) -> Result { 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)?, - total_length: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.total_length)?, + 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)?, @@ -80,7 +80,7 @@ pub fn info(p: &Process, state: &mut State) -> Result { 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)?, - total_length: p.read_i32(beatmap_addr + BEATMAP_OFFSET.stats.total_length)?, + 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)?, diff --git a/src/reader/common/mod.rs b/src/reader/common/mod.rs index 8401b8a..4b9cbe7 100644 --- a/src/reader/common/mod.rs +++ b/src/reader/common/mod.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::Process; +use crate::impl_osu_accessor; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] pub enum OsuClientKind { @@ -120,41 +121,16 @@ impl<'a> CommonReader<'a> { } } - pub fn game_state(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::game_state(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn menu_game_mode(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::menu_game_mode(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn path_folder(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::path_folder(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } + impl_osu_accessor! { + fn game_state() -> GameState => stable::memory::game_state, + fn menu_game_mode() -> u32 => stable::memory::menu_game_mode, + fn path_folder() -> PathBuf => stable::memory::path_folder, } pub fn check_game_state(&mut self, g_state: GameState) -> Result { match self.osu_type { - OsuClientKind::Stable => { - stable::memory::check_game_state(self.process, self.state, g_state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), + OsuClientKind::Stable => stable::memory::check_game_state(self.process, self.state, g_state), + _ => Err(Error::Unsupported("Unsupported osu type for now".to_string())), } } } diff --git a/src/reader/common/stable/memory.rs b/src/reader/common/stable/memory.rs index 0933955..3e6470e 100644 --- a/src/reader/common/stable/memory.rs +++ b/src/reader/common/stable/memory.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use crate::reader::common::stable::offset::COMMON_OFFSET; -use crate::reader::common::{GameMode, GameState}; +use crate::reader::common::GameState; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; @@ -45,7 +45,7 @@ pub fn playtime_addr(p: &Process, state: &mut State) -> Result { generate_offset_getter! { game_state: GameState = read_u32(0, status_addr); - menu_game_mode: GameMode = read_i32(COMMON_OFFSET.mods_ptr, menu_mods_addr); + menu_game_mode: u32 = read_u32(0, menu_mods_addr); // TODO: use GameModsLegacy game_time: i32 = read_i32(0, playtime_addr); } diff --git a/src/reader/gameplay/mod.rs b/src/reader/gameplay/mod.rs index 563c15e..7a0adf0 100644 --- a/src/reader/gameplay/mod.rs +++ b/src/reader/gameplay/mod.rs @@ -1,6 +1,7 @@ pub mod common; pub mod stable; +use crate::impl_osu_accessor; use crate::reader::common::OsuClientKind; use crate::reader::gameplay::common::GameplayInfo; use crate::reader::structs::Hit; @@ -21,148 +22,23 @@ impl<'a> GameplayReader<'a> { osu_type, } } + impl_osu_accessor!{ + fn score() -> i32 => stable::memory::score, + fn mods() -> u32 => stable::memory::mods, + fn combo() -> i16 => stable::memory::combo, + fn max_combo() -> i16 => stable::memory::max_combo, + fn hp() -> f64 => stable::memory::hp, + fn username() -> String => stable::memory::username, + fn game_time() -> i32 => stable::memory::game_time, + fn retries() -> i32 => stable::memory::retries, + fn hits() -> Hit => stable::memory::hits, + fn hits_300() -> i16 => stable::memory::hits_300, + fn hits_100() -> i16 => stable::memory::hits_100, + fn hits_50() -> i16 => stable::memory::hits_50, + fn hits_miss() -> i16 => stable::memory::hits_miss, + fn hits_geki() -> i16 => stable::memory::hits_geki, + fn hits_katu() -> i16 => stable::memory::hits_katu, + fn info() -> GameplayInfo => stable::memory::info, - pub fn score(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::score(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn mods(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::mods(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn combo(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::combo(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn max_combo(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::max_combo(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hp(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hp(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn username(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::username(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn game_time(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => crate::reader::common::stable::memory::game_time(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn retries(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::retries(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hits_300(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hits_300(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hits_100(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hits_100(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hits_50(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hits_50(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hits_miss(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hits_miss(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hits_geki(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hits_geki(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hits_katu(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hits_katu(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn hits(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::hits(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn gameplay_info(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::info(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } } } diff --git a/src/reader/helpers.rs b/src/reader/helpers.rs index b945bc1..8cb9c95 100644 --- a/src/reader/helpers.rs +++ b/src/reader/helpers.rs @@ -1,6 +1,8 @@ use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; +use crate::reader::common::GameMode; +use crate::reader::structs::Hit; macro_rules! generate_reader_fn { ( @@ -24,6 +26,7 @@ generate_reader_fn!(read_u32, u32, read_u32); generate_reader_fn!(read_u64, u64, read_u64); generate_reader_fn!(read_f32, f32, read_f32); generate_reader_fn!(read_f64, f64, read_f64); + #[macro_export] macro_rules! generate_offset_getter { ( @@ -35,4 +38,67 @@ macro_rules! generate_offset_getter { } )* }; +} +// macro to gen wrappers for reader +#[macro_export] +macro_rules! impl_osu_accessor { + ($(fn $name:ident() -> $ret:ty => $call:path),* $(,)?) => { + $( + pub fn $name(&mut self) -> Result<$ret, Error> { + match self.osu_type { + OsuClientKind::Stable => $call(self.process, self.state), + _ => Err(Error::Unsupported( + "Unsupported osu type for now".to_string(), + )), + } + } + )* + }; +} + + + +// TODO : idk where to put this +#[inline] +pub fn calculate_accuracy(gamemode: &GameMode, hit: &Hit) -> Result { + let acc = match gamemode { + GameMode::Osu => { + let total = (hit._300 + hit._100 + hit._50 + hit._miss) as f64; + if total == 0.0 { + return Ok(0.0); + } + let score = hit._300 as f64 * 6.0 + hit._100 as f64 * 2.0 + hit._50 as f64; + (score / (total * 6.0)) * 100.0 + } + GameMode::Taiko => { + let total = (hit._300 + hit._100 + hit._50 + hit._miss) as f64; + if total == 0.0 { + return Ok(0.0); + } + let score = hit._300 as f64 * 2.0 + hit._100 as f64; + (score / (total * 2.0)) * 100.0 + } + GameMode::Catch => { + let caught = (hit._300 + hit._100 + hit._50) as f64; + let total = (hit._300 + hit._100 + hit._50 + hit._katu + hit._miss) as f64; + if total == 0.0 { + return Ok(0.0); + } + (caught / total) * 100.0 + } + GameMode::Mania => { + let total = (hit._geki + hit._300 + hit._katu + hit._100 + hit._50 + hit._miss) as f64; + if total == 0.0 { + return Ok(0.0); + } + let score = (hit._geki + hit._300) as f64 * 6.0 + + hit._katu as f64 * 4.0 + + hit._100 as f64 * 2.0 + + hit._50 as f64; + (score / (total * 6.0)) * 100.0 + } + _ => return Ok(0.0), + }; + + Ok(acc) } \ No newline at end of file diff --git a/src/reader/resultscreen/common.rs b/src/reader/resultscreen/common.rs index 5af4878..6da4174 100644 --- a/src/reader/resultscreen/common.rs +++ b/src/reader/resultscreen/common.rs @@ -7,6 +7,6 @@ pub struct ResultScreenInfo { pub mode: GameMode, pub max_combo: i16, pub score: i32, - pub hit: Hit, + pub hits: Hit, pub accuracy: f64, } diff --git a/src/reader/resultscreen/mod.rs b/src/reader/resultscreen/mod.rs index 6b9b5fa..8d6cdde 100644 --- a/src/reader/resultscreen/mod.rs +++ b/src/reader/resultscreen/mod.rs @@ -7,7 +7,7 @@ use crate::reader::structs::Hit; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::Process; - +use crate::impl_osu_accessor; pub struct ResultScreenReader<'a> { pub process: &'a Process, pub state: &'a mut State, @@ -22,123 +22,19 @@ impl<'a> ResultScreenReader<'a> { osu_type, } } - - pub fn get_result_screen_info(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => { - stable::memory::get_result_screen_info(self.process, self.state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_username(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_username(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_score(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_score(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_mode(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_mode(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_hit_300(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_hit_300(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_hit_100(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_hit_100(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_hit_50(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_hit_50(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_hit_miss(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_hit_miss(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_hit_geki(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_hit_geki(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_hit_katu(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_hit_katu(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_hits(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_hits(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_accuracy(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_accuracy(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_result_max_combo(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_result_max_combo(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } + impl_osu_accessor! { + fn username() -> String => stable::memory::username, + fn score() -> i32 => stable::memory::score, + fn mode() -> GameMode => stable::memory::mode, + fn max_combo() -> i16 => stable::memory::max_combo, + fn hits() -> Hit => stable::memory::hits, + fn hits_300() -> i16 => stable::memory::hits_300, + fn hits_100() -> i16 => stable::memory::hits_100, + fn hits_50() -> i16 => stable::memory::hits_50, + fn hits_miss() -> i16 => stable::memory::hits_miss, + fn hits_geki() -> i16 => stable::memory::hits_geki, + fn hits_katu() -> i16 => stable::memory::hits_katu, + fn accuracy() -> f64 => stable::memory::accuracy, + fn info() -> ResultScreenInfo => stable::memory::info, } } diff --git a/src/reader/resultscreen/stable/memory.rs b/src/reader/resultscreen/stable/memory.rs index 7f944fd..1e26973 100644 --- a/src/reader/resultscreen/stable/memory.rs +++ b/src/reader/resultscreen/stable/memory.rs @@ -6,65 +6,19 @@ use crate::reader::resultscreen::stable::offset::RESULT_SCREEN_OFFSET; use crate::reader::structs::{Hit, State}; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; +use crate::generate_offset_getter; +use crate::reader::helpers::{calculate_accuracy, read_i16, read_i32, read_string}; -pub(crate) fn get_score_base(p: &Process, state: &mut State) -> Result { +pub fn result_screen_ptr(p: &Process, state: &mut State) -> Result { if check_game_state(p, state, GameState::ResultScreen)? { - let ruleset_addr = match p.read_i32(state.addresses.rulesets - RESULT_SCREEN_OFFSET.ptr) { - Ok(val) => val, - Err(_) => return Err(Error::NotAvailable("Still loading".to_string())), - }; - let ruleset_addr = match p.read_i32(ruleset_addr + RESULT_SCREEN_OFFSET.addr) { - Ok(val) => val, - Err(_) => return Err(Error::NotAvailable("Still loading".to_string())), - }; - Ok(p.read_i32(ruleset_addr + RESULT_SCREEN_OFFSET.score_base)?) + Ok(p.read_i32(state.addresses.rulesets - RESULT_SCREEN_OFFSET.ptr)?) } else { Err(Error::NotAvailable("Not in ResultScreen".to_string())) } } -pub fn get_result_username(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_string(score_base + RESULT_SCREEN_OFFSET.username)?) -} -pub fn get_result_score(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i32(score_base + RESULT_SCREEN_OFFSET.score)?) -} -pub fn get_result_mode(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(GameMode::from( - p.read_i32(score_base + RESULT_SCREEN_OFFSET.mode)?, - )) -} - -pub fn get_result_hit_300(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._300)?) -} -pub fn get_result_hit_100(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._100)?) -} -pub fn get_result_hit_50(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._50)?) -} -pub fn get_result_hit_geki(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._geki)?) -} -pub fn get_result_hit_katu(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._katu)?) -} -pub fn get_result_hit_miss(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._miss)?) -} - -pub fn get_result_hits(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; +pub fn hits(p: &Process, state: &mut State) -> Result { + let score_base = result_screen_base(p, state)?; Ok(Hit { _300: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._300)?, _100: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._100)?, @@ -74,53 +28,39 @@ pub fn get_result_hits(p: &Process, state: &mut State) -> Result { _katu: p.read_i16(score_base + RESULT_SCREEN_OFFSET.hits._katu)?, }) } -fn calculate_accuracy(gamemode: &GameMode, hit: &Hit) -> Result { - let (numerator, denominator) = match gamemode { - GameMode::Osu => ( - hit._300 as f64 * 6.0 + hit._100 as f64 * 2.0 + hit._50 as f64, - (hit._300 + hit._100 + hit._50 + hit._miss) as f64 * 6.0, - ), - GameMode::Taiko => ( - hit._300 as f64 * 2.0 + hit._100 as f64, - (hit._300 + hit._100 + hit._50 + hit._miss) as f64 * 2.0, - ), - GameMode::Catch => ( - (hit._300 + hit._100 + hit._50) as f64, - (hit._300 + hit._100 + hit._50 + hit._katu + hit._miss) as f64, - ), - GameMode::Mania => ( - (hit._geki + hit._300) as f64 * 6.0 - + hit._katu as f64 * 4.0 - + hit._100 as f64 * 2.0 - + hit._50 as f64, - (hit._geki + hit._300 + hit._katu + hit._100 + hit._50 + hit._miss) as f64 * 6.0, - ), - _ => (0.0, 0.0), - }; - Ok((numerator / denominator) * 100.0) +pub fn accuracy(p: &Process, state: &mut State) -> Result { + calculate_accuracy(&mode(p, state)?, &hits(p, state)?) } -pub fn get_result_accuracy(p: &Process, state: &mut State) -> Result { - calculate_accuracy(&get_result_mode(p, state)?, &get_result_hits(p, state)?) -} -pub fn get_result_max_combo(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - Ok(p.read_i16(score_base + RESULT_SCREEN_OFFSET.max_combo)?) +generate_offset_getter! { + result_screen_addr: i32 = read_i32(RESULT_SCREEN_OFFSET.addr, result_screen_ptr); + result_screen_base: i32 = read_i32(RESULT_SCREEN_OFFSET.base, result_screen_addr); + username: String = read_string(RESULT_SCREEN_OFFSET.username, result_screen_base); + score: i32 = read_i32(RESULT_SCREEN_OFFSET.score, result_screen_base); + max_combo: i16 = read_i16(RESULT_SCREEN_OFFSET.max_combo, result_screen_base); + mode: GameMode = read_i32(RESULT_SCREEN_OFFSET.mode, result_screen_base); + hits_300: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._300, result_screen_base); + hits_100: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._100, result_screen_base); + hits_50: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._50, result_screen_base); + hits_miss: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._miss, result_screen_base); + hits_geki: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._geki, result_screen_base); + hits_katu: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._katu, result_screen_base); } -pub fn get_result_screen_info(p: &Process, state: &mut State) -> Result { - let score_base = get_score_base(p, state)?; - let hit = get_result_hits(p, state)?; - let mode = get_result_mode(p, state)?; - let accuracy = calculate_accuracy(&mode, &hit)?; + +pub fn info(p: &Process, state: &mut State) -> Result { + let hits = hits(p, state)?; + let mode = mode(p, state)?; + let accuracy = calculate_accuracy(&mode, &hits)?; + let base = result_screen_base(p, state)?; Ok(ResultScreenInfo { - username: p.read_string(score_base + RESULT_SCREEN_OFFSET.username)?, + username: p.read_string(base + RESULT_SCREEN_OFFSET.username)?, mode, - max_combo: p.read_i16(score_base + RESULT_SCREEN_OFFSET.max_combo)?, - score: p.read_i32(score_base + RESULT_SCREEN_OFFSET.score)?, - hit, + max_combo: p.read_i16(base + RESULT_SCREEN_OFFSET.max_combo)?, + score: p.read_i32(base + RESULT_SCREEN_OFFSET.score)?, + hits, accuracy, }) } diff --git a/src/reader/resultscreen/stable/offset.rs b/src/reader/resultscreen/stable/offset.rs index 502c507..e66b4b8 100644 --- a/src/reader/resultscreen/stable/offset.rs +++ b/src/reader/resultscreen/stable/offset.rs @@ -1,7 +1,7 @@ pub struct ResultScreenOffset { pub ptr: i32, pub addr: i32, - pub score_base: i32, + pub base: i32, pub username: i32, pub score: i32, pub max_combo: i32, @@ -12,7 +12,7 @@ pub struct ResultScreenOffset { pub(crate) const RESULT_SCREEN_OFFSET: ResultScreenOffset = ResultScreenOffset { ptr: 0xb, addr: 0x4, - score_base: 0x38, + base: 0x38, username: 0x28, score: 0x78, max_combo: 0x68, From 05ee4f85990e0bbe5228504426f5bf8ebdd80365 Mon Sep 17 00:00:00 2001 From: Glubus <50545507+Glubus@users.noreply.github.com> Date: Sun, 13 Jul 2025 12:50:06 +0200 Subject: [PATCH 6/6] refactor: MACROS FEATURE IS DONE POG --- Cargo.lock | 2 +- Cargo.toml | 2 +- examples/beatmap3.rs | 206 +++++++++++------------ examples/gameplay.rs | 2 +- examples/pp.rs | 4 +- examples/resultscreen.rs | 64 +++++++ examples/user.rs | 63 +++++++ src/reader/beatmap/mod.rs | 2 +- src/reader/beatmap/stable/file.rs | 2 - src/reader/beatmap/stable/memory.rs | 4 +- src/reader/common/mod.rs | 10 +- src/reader/common/stable/memory.rs | 5 +- src/reader/gameplay/mod.rs | 2 +- src/reader/gameplay/stable/memory.rs | 18 +- src/reader/helpers.rs | 9 +- src/reader/resultscreen/mod.rs | 2 +- src/reader/resultscreen/stable/memory.rs | 6 +- src/reader/user/mod.rs | 126 ++------------ src/reader/user/stable/memory.rs | 77 ++------- 19 files changed, 295 insertions(+), 311 deletions(-) create mode 100644 examples/resultscreen.rs create mode 100644 examples/user.rs diff --git a/Cargo.lock b/Cargo.lock index 5f40141..7b5cca6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,7 +115,7 @@ dependencies = [ [[package]] name = "rosu-memory-lib" -version = "1.1.1" +version = "1.2.0" dependencies = [ "rayon", "rosu-map", diff --git a/Cargo.toml b/Cargo.toml index ea63b56..be521be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rosu-memory-lib" -version = "1.1.1" +version = "1.2.0" edition = "2021" description = "A library to read osu! memory" license = "MIT" diff --git a/examples/beatmap3.rs b/examples/beatmap3.rs index 15c0089..71adf76 100644 --- a/examples/beatmap3.rs +++ b/examples/beatmap3.rs @@ -6,109 +6,109 @@ use rosu_memory_lib::Error; fn main() -> Result<(), Error> { let (mut state, process) = init_loop(500)?; let mut beatmap_reader = BeatmapReader::new(&process, &mut state, OsuClientKind::Stable)?; - match beatmap_reader.audio_path() { - Ok(audio_path) => println!("Current beatmap audio path: {audio_path:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.path() { - Ok(path) => println!("Current beatmap path: {path:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.md5() { - Ok(md5) => println!("Current beatmap md5: {md5:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.id() { - Ok(id) => println!("Current beatmap id: {id:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.set_id() { - Ok(set_id) => println!("Current beatmap set id: {set_id:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.mode() { - Ok(mode) => println!("Current beatmap mode: {mode:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.tags() { - Ok(tags) => println!("Current beatmap tags: {tags:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.length() { - Ok(length) => println!("Current beatmap length: {length:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.drain_time() { - Ok(drain_time) => println!("Current beatmap drain time: {drain_time:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.author() { - Ok(author) => println!("Current beatmap author: {author:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.creator() { - Ok(creator) => println!("Current beatmap creator: {creator:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.title_romanized() { - Ok(title_romanized) => println!("Current beatmap title romanized: {title_romanized:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.title() { - Ok(title) => println!("Current beatmap title: {title:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.difficulty() { - Ok(difficulty) => println!("Current beatmap difficulty: {difficulty:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.od() { - Ok(od) => println!("Current beatmap od: {od:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.ar() { - Ok(ar) => println!("Current beatmap ar: {ar:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.cs() { - Ok(cs) => println!("Current beatmap cs: {cs:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.hp() { - Ok(hp) => println!("Current beatmap hp: {hp:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.object_count() { - Ok(object_count) => println!("Current beatmap object count: {object_count:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.slider_count() { - Ok(slider_count) => println!("Current beatmap slider count: {slider_count:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.folder() { - Ok(folder) => println!("Current beatmap folder: {folder:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.filename() { - Ok(filename) => println!("Current beatmap filename: {filename:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.audio() { - Ok(audio) => println!("Current beatmap audio: {audio:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.cover() { - Ok(cover) => println!("Current beatmap cover: {cover:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.status() { - Ok(status) => println!("Current beatmap status: {status:?}"), - Err(e) => println!("Error: {e:?}"), - } - match beatmap_reader.star_rating() { - Ok(star_rating) => println!("Current beatmap star rating: {star_rating:?}"), - Err(e) => println!("Error: {e:?}"), + match beatmap_reader.audio_path() { + Ok(audio_path) => println!("Current beatmap audio path: {audio_path:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.path() { + Ok(path) => println!("Current beatmap path: {path:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.md5() { + Ok(md5) => println!("Current beatmap md5: {md5:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.id() { + Ok(id) => println!("Current beatmap id: {id:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.set_id() { + Ok(set_id) => println!("Current beatmap set id: {set_id:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.mode() { + Ok(mode) => println!("Current beatmap mode: {mode:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.tags() { + Ok(tags) => println!("Current beatmap tags: {tags:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.length() { + Ok(length) => println!("Current beatmap length: {length:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.drain_time() { + Ok(drain_time) => println!("Current beatmap drain time: {drain_time:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.author() { + Ok(author) => println!("Current beatmap author: {author:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.creator() { + Ok(creator) => println!("Current beatmap creator: {creator:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.title_romanized() { + Ok(title_romanized) => println!("Current beatmap title romanized: {title_romanized:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.title() { + Ok(title) => println!("Current beatmap title: {title:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.difficulty() { + Ok(difficulty) => println!("Current beatmap difficulty: {difficulty:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.od() { + Ok(od) => println!("Current beatmap od: {od:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.ar() { + Ok(ar) => println!("Current beatmap ar: {ar:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.cs() { + Ok(cs) => println!("Current beatmap cs: {cs:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.hp() { + Ok(hp) => println!("Current beatmap hp: {hp:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.object_count() { + Ok(object_count) => println!("Current beatmap object count: {object_count:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.slider_count() { + Ok(slider_count) => println!("Current beatmap slider count: {slider_count:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.folder() { + Ok(folder) => println!("Current beatmap folder: {folder:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.filename() { + Ok(filename) => println!("Current beatmap filename: {filename:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.audio() { + Ok(audio) => println!("Current beatmap audio: {audio:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.cover() { + Ok(cover) => println!("Current beatmap cover: {cover:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.status() { + Ok(status) => println!("Current beatmap status: {status:?}"), + Err(e) => println!("Error: {e:?}"), + } + match beatmap_reader.star_rating() { + Ok(star_rating) => println!("Current beatmap star rating: {star_rating:?}"), + Err(e) => println!("Error: {e:?}"), } Ok(()) } diff --git a/examples/gameplay.rs b/examples/gameplay.rs index 73798bd..b1844d7 100644 --- a/examples/gameplay.rs +++ b/examples/gameplay.rs @@ -1,6 +1,6 @@ use rosu_memory_lib::init_loop; -use rosu_memory_lib::reader::gameplay::GameplayReader; use rosu_memory_lib::reader::common::OsuClientKind; +use rosu_memory_lib::reader::gameplay::GameplayReader; use rosu_memory_lib::Error; fn main() -> Result<(), Error> { diff --git a/examples/pp.rs b/examples/pp.rs index d711549..b821f57 100644 --- a/examples/pp.rs +++ b/examples/pp.rs @@ -105,11 +105,11 @@ fn process_game_state( println!("Menu game mode: {}", menu_game_mode(process, state)?); if let Ok(new_mods) = menu_game_mode(process, state) { mods_changed = calc_state.update_mods(new_mods as i32); - } + } // Update beatmap if path changed and mods changed else it's useless to recalculate let beatmap_updated = calc_state.update_beatmap(beatmap_path)?; - + if beatmap_updated || mods_changed { calc_state.update_pp(); } diff --git a/examples/resultscreen.rs b/examples/resultscreen.rs new file mode 100644 index 0000000..386b0ac --- /dev/null +++ b/examples/resultscreen.rs @@ -0,0 +1,64 @@ +use rosu_memory_lib::init_loop; +use rosu_memory_lib::reader::common::OsuClientKind; +use rosu_memory_lib::reader::resultscreen::ResultScreenReader; +use rosu_memory_lib::Error; + +fn main() -> Result<(), Error> { + let (mut state, process) = init_loop(500)?; + let mut resultscreen_reader = + ResultScreenReader::new(&process, &mut state, OsuClientKind::Stable); + match resultscreen_reader.accuracy() { + Ok(accuracy) => println!("Current accuracy: {accuracy}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.max_combo() { + Ok(max_combo) => println!("Current max combo: {max_combo}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.score() { + Ok(score) => println!("Current score: {score}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.username() { + Ok(username) => println!("Current username: {username}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.mode() { + Ok(mode) => println!("Current mode: {mode:?}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.hits_miss() { + Ok(hits_miss) => println!("Current hits miss: {hits_miss}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.hits_geki() { + Ok(hits_geki) => println!("Current hits geki: {hits_geki}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.hits_katu() { + Ok(hits_katu) => println!("Current hits katu: {hits_katu}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.hits_300() { + Ok(hits_300) => println!("Current hits 300: {hits_300}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.hits_100() { + Ok(hits_100) => println!("Current hits 100: {hits_100}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.hits_50() { + Ok(hits_50) => println!("Current hits 50: {hits_50}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.hits() { + Ok(hits) => println!("Current hits: {hits:?}"), + Err(e) => println!("Error: {e:?}"), + } + match resultscreen_reader.info() { + Ok(resultscreen_info) => println!("Current resultscreen info: {resultscreen_info:?}"), + Err(e) => println!("Error: {e:?}"), + } + + Ok(()) +} diff --git a/examples/user.rs b/examples/user.rs new file mode 100644 index 0000000..8984425 --- /dev/null +++ b/examples/user.rs @@ -0,0 +1,63 @@ +use rosu_memory_lib::init_loop; +use rosu_memory_lib::reader::common::OsuClientKind; +use rosu_memory_lib::reader::user::UserReader; +use rosu_memory_lib::Error; + +fn main() -> Result<(), Error> { + let (mut state, process) = init_loop(500)?; + let mut user_reader = UserReader::new(&process, &mut state, OsuClientKind::Stable); + match user_reader.info() { + Ok(user_info) => println!("Current user info: {user_info:?}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.username() { + Ok(username) => println!("Current username: {username}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.pp() { + Ok(pp) => println!("Current pp: {pp}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.accuracy() { + Ok(accuracy) => println!("Current accuracy: {accuracy}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.playcount() { + Ok(playcount) => println!("Current playcount: {playcount}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.rank() { + Ok(rank) => println!("Current rank: {rank}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.playmode() { + Ok(playmode) => println!("Current playmode: {playmode}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.country_code() { + Ok(country_code) => println!("Current country code: {country_code}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.bancho_status() { + Ok(bancho_status) => println!("Current bancho status: {bancho_status}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.id() { + Ok(id) => println!("Current id: {id}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.rankedscore() { + Ok(rankedscore) => println!("Current rankedscore: {rankedscore}"), + Err(e) => println!("Error: {e:?}"), + } + match user_reader.level() { + 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(()) +} \ No newline at end of file diff --git a/src/reader/beatmap/mod.rs b/src/reader/beatmap/mod.rs index 917c596..1aa3993 100644 --- a/src/reader/beatmap/mod.rs +++ b/src/reader/beatmap/mod.rs @@ -3,6 +3,7 @@ pub mod stable; use std::path::PathBuf; +use crate::impl_osu_accessor; use crate::reader::beatmap::common::BeatmapInfo; use crate::reader::beatmap::common::BeatmapStarRating; use crate::reader::beatmap::common::BeatmapStats; @@ -12,7 +13,6 @@ use crate::reader::common::OsuClientKind; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::Process; -use crate::impl_osu_accessor; pub struct BeatmapReader<'a> { pub process: &'a Process, diff --git a/src/reader/beatmap/stable/file.rs b/src/reader/beatmap/stable/file.rs index 5199c9b..cea5019 100644 --- a/src/reader/beatmap/stable/file.rs +++ b/src/reader/beatmap/stable/file.rs @@ -30,7 +30,6 @@ pub fn audio_path(p: &Process, state: &mut State) -> Result { Ok(songs_path.join(folder).join(audio)) } - // generate getters that use the default logic macro_rules! generate_beatmap_field_getter { ( @@ -111,7 +110,6 @@ generate_beatmap_custom_getter_safe! { } - // cant do this in file mode pub fn status(p: &Process, state: &mut State) -> Result { // cant do this in file mode diff --git a/src/reader/beatmap/stable/memory.rs b/src/reader/beatmap/stable/memory.rs index 1557943..3d7e547 100644 --- a/src/reader/beatmap/stable/memory.rs +++ b/src/reader/beatmap/stable/memory.rs @@ -1,4 +1,5 @@ use crate::common::GameMode; +use crate::generate_offset_getter; use crate::reader::beatmap::common::{ BeatmapInfo, BeatmapLocation, BeatmapMetadata, BeatmapStats, BeatmapStatus, BeatmapTechnicalInfo, @@ -8,7 +9,6 @@ use crate::reader::helpers::{read_f32, read_i32, read_string, read_u32}; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; -use crate::generate_offset_getter; generate_offset_getter! { id: i32 = read_i32(BEATMAP_OFFSET.technical.id, beatmap_addr); @@ -36,8 +36,6 @@ generate_offset_getter! { status: BeatmapStatus = read_i32(BEATMAP_OFFSET.technical.ranked_status, beatmap_addr); } - - pub fn stats(p: &Process, state: &mut State) -> Result { let beatmap_addr = beatmap_addr(p, state)?; // faster than using read_fn because we dont need to reload addr everytime diff --git a/src/reader/common/mod.rs b/src/reader/common/mod.rs index 4b9cbe7..360ba81 100644 --- a/src/reader/common/mod.rs +++ b/src/reader/common/mod.rs @@ -1,10 +1,10 @@ pub mod stable; use std::path::PathBuf; +use crate::impl_osu_accessor; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::Process; -use crate::impl_osu_accessor; #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] pub enum OsuClientKind { @@ -129,8 +129,12 @@ impl<'a> CommonReader<'a> { pub fn check_game_state(&mut self, g_state: GameState) -> Result { match self.osu_type { - OsuClientKind::Stable => stable::memory::check_game_state(self.process, self.state, g_state), - _ => Err(Error::Unsupported("Unsupported osu type for now".to_string())), + OsuClientKind::Stable => { + stable::memory::check_game_state(self.process, self.state, g_state) + } + _ => Err(Error::Unsupported( + "Unsupported osu type for now".to_string(), + )), } } } diff --git a/src/reader/common/stable/memory.rs b/src/reader/common/stable/memory.rs index 3e6470e..c579818 100644 --- a/src/reader/common/stable/memory.rs +++ b/src/reader/common/stable/memory.rs @@ -1,18 +1,17 @@ use std::path::PathBuf; +use crate::generate_offset_getter; use crate::reader::common::stable::offset::COMMON_OFFSET; use crate::reader::common::GameState; +use crate::reader::helpers::{read_i32, read_u32}; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; -use crate::generate_offset_getter; -use crate::reader::helpers::{read_i32, read_u32}; pub fn status_addr(p: &Process, state: &mut State) -> Result { Ok(p.read_i32(state.addresses.status - COMMON_OFFSET.status)?) } - /// Returns a path to the `Songs` folder /// /// **Platform-specific** diff --git a/src/reader/gameplay/mod.rs b/src/reader/gameplay/mod.rs index 7a0adf0..c6df7d2 100644 --- a/src/reader/gameplay/mod.rs +++ b/src/reader/gameplay/mod.rs @@ -22,7 +22,7 @@ impl<'a> GameplayReader<'a> { osu_type, } } - impl_osu_accessor!{ + impl_osu_accessor! { fn score() -> i32 => stable::memory::score, fn mods() -> u32 => stable::memory::mods, fn combo() -> i16 => stable::memory::combo, diff --git a/src/reader/gameplay/stable/memory.rs b/src/reader/gameplay/stable/memory.rs index 1c6c090..177b7d3 100644 --- a/src/reader/gameplay/stable/memory.rs +++ b/src/reader/gameplay/stable/memory.rs @@ -1,12 +1,15 @@ -use crate::reader::common::stable::memory::{check_game_state}; +use crate::reader::common::stable::memory::check_game_state; use crate::reader::common::GameState; use crate::reader::gameplay::common::GameplayInfo; use crate::reader::gameplay::stable::offset::GAMEPLAY_OFFSET; use crate::reader::structs::Hit; use crate::reader::structs::State; use crate::Error; +use crate::{ + generate_offset_getter, + reader::helpers::{read_f64, read_i16, read_i32, read_string, read_u64}, +}; use rosu_mem::process::{Process, ProcessTraits}; -use crate::{generate_offset_getter, reader::helpers::{read_f64, read_i16, read_i32, read_string, read_u64}}; pub fn rulesets_addr(p: &Process, state: &mut State) -> Result { if check_game_state(p, state, GameState::Playing)? { @@ -43,10 +46,7 @@ generate_offset_getter! { hits_katu: i16 = read_i16(GAMEPLAY_OFFSET.hits._katu, score_base); } - - - -/// this is a wrapper to not confuse people it could be deleted in the future +/// this is a wrapper to not confuse people it could be deleted in the future /// use -> crate::reader::common::stable::memory::game_time pub fn game_time(p: &Process, state: &mut State) -> Result { crate::reader::common::stable::memory::game_time(p, state) @@ -60,8 +60,8 @@ pub fn retries(p: &Process, state: &mut State) -> Result { pub fn hits(p: &Process, state: &mut State) -> Result { let score_base = score_base(p, state)?; - // TODO: check issue for reading the full block and - // separating bits + // TODO: check issue for reading the full block and + // separating bits Ok(Hit { _300: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._300)?, _100: p.read_i16(score_base + GAMEPLAY_OFFSET.hits._100)?, @@ -86,7 +86,7 @@ pub fn info(p: &Process, state: &mut State) -> Result { hp, username: p.read_string(score_base + GAMEPLAY_OFFSET.username)?, ig_time: game_time(p, state)?, // different base - retries: retries(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)?, diff --git a/src/reader/helpers.rs b/src/reader/helpers.rs index 8cb9c95..8c8867c 100644 --- a/src/reader/helpers.rs +++ b/src/reader/helpers.rs @@ -1,8 +1,8 @@ +use crate::reader::common::GameMode; +use crate::reader::structs::Hit; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; -use crate::reader::common::GameMode; -use crate::reader::structs::Hit; macro_rules! generate_reader_fn { ( @@ -23,6 +23,7 @@ generate_reader_fn!(read_string, String, read_string); generate_reader_fn!(read_i16, i16, read_i16); generate_reader_fn!(read_i32, i32, read_i32); generate_reader_fn!(read_u32, u32, read_u32); +generate_reader_fn!(read_i64, i64, read_i64); generate_reader_fn!(read_u64, u64, read_u64); generate_reader_fn!(read_f32, f32, read_f32); generate_reader_fn!(read_f64, f64, read_f64); @@ -56,8 +57,6 @@ macro_rules! impl_osu_accessor { }; } - - // TODO : idk where to put this #[inline] pub fn calculate_accuracy(gamemode: &GameMode, hit: &Hit) -> Result { @@ -101,4 +100,4 @@ pub fn calculate_accuracy(gamemode: &GameMode, hit: &Hit) -> Result }; Ok(acc) -} \ No newline at end of file +} diff --git a/src/reader/resultscreen/mod.rs b/src/reader/resultscreen/mod.rs index 8d6cdde..c48b939 100644 --- a/src/reader/resultscreen/mod.rs +++ b/src/reader/resultscreen/mod.rs @@ -1,5 +1,6 @@ pub mod common; pub mod stable; +use crate::impl_osu_accessor; use crate::reader::common::GameMode; use crate::reader::common::OsuClientKind; use crate::reader::resultscreen::common::ResultScreenInfo; @@ -7,7 +8,6 @@ use crate::reader::structs::Hit; use crate::reader::structs::State; use crate::Error; use rosu_mem::process::Process; -use crate::impl_osu_accessor; pub struct ResultScreenReader<'a> { pub process: &'a Process, pub state: &'a mut State, diff --git a/src/reader/resultscreen/stable/memory.rs b/src/reader/resultscreen/stable/memory.rs index 1e26973..95e9a4c 100644 --- a/src/reader/resultscreen/stable/memory.rs +++ b/src/reader/resultscreen/stable/memory.rs @@ -1,13 +1,13 @@ +use crate::generate_offset_getter; use crate::reader::common::stable::memory::check_game_state; use crate::reader::common::GameMode; use crate::reader::common::GameState; +use crate::reader::helpers::{calculate_accuracy, read_i16, read_i32, read_string}; use crate::reader::resultscreen::common::ResultScreenInfo; use crate::reader::resultscreen::stable::offset::RESULT_SCREEN_OFFSET; use crate::reader::structs::{Hit, State}; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; -use crate::generate_offset_getter; -use crate::reader::helpers::{calculate_accuracy, read_i16, read_i32, read_string}; pub fn result_screen_ptr(p: &Process, state: &mut State) -> Result { if check_game_state(p, state, GameState::ResultScreen)? { @@ -48,8 +48,6 @@ generate_offset_getter! { hits_katu: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._katu, result_screen_base); } - - pub fn info(p: &Process, state: &mut State) -> Result { let hits = hits(p, state)?; let mode = mode(p, state)?; diff --git a/src/reader/user/mod.rs b/src/reader/user/mod.rs index 01d00e6..061fa3b 100644 --- a/src/reader/user/mod.rs +++ b/src/reader/user/mod.rs @@ -1,11 +1,11 @@ pub mod common; pub mod stable; +use crate::impl_osu_accessor; use crate::reader::common::OsuClientKind; use crate::reader::structs::State; use crate::reader::user::common::UserInfo; use crate::Error; use rosu_mem::process::Process; - pub struct UserReader<'a> { pub process: &'a Process, pub state: &'a mut State, @@ -20,116 +20,18 @@ impl<'a> UserReader<'a> { osu_type, } } - - pub fn get_user_info(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_info(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_id(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_id(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_bancho_status(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => { - stable::memory::get_user_bancho_status(self.process, self.state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_country_code(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => { - stable::memory::get_user_country_code(self.process, self.state) - } - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_username(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_username(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_pp(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_pp(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_rankedscore(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_rankedscore(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_level(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_level(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_playcount(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_playcount(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_rank(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_rank(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_playmode(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_playmode(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } - } - - pub fn get_user_accuracy(&mut self) -> Result { - match self.osu_type { - OsuClientKind::Stable => stable::memory::get_user_accuracy(self.process, self.state), - _ => Err(Error::Unsupported( - "Unsupported osu type for now".to_string(), - )), - } + impl_osu_accessor! { + fn id() -> i32 => stable::memory::id, + fn bancho_status() -> i32 => stable::memory::bancho_status, + fn country_code() -> i32 => stable::memory::country_code, + fn username() -> String => stable::memory::username, + fn pp() -> i32 => stable::memory::pp, + fn rankedscore() -> i64 => stable::memory::rankedscore, + fn level() -> f32 => stable::memory::level, + fn playcount() -> i32 => stable::memory::playcount, + fn rank() -> i32 => stable::memory::rank, + fn playmode() -> i32 => stable::memory::playmode, + fn accuracy() -> f64 => stable::memory::accuracy, + fn info() -> UserInfo => stable::memory::info, } } diff --git a/src/reader/user/stable/memory.rs b/src/reader/user/stable/memory.rs index a19aba4..fb361e6 100644 --- a/src/reader/user/stable/memory.rs +++ b/src/reader/user/stable/memory.rs @@ -1,75 +1,34 @@ +use crate::generate_offset_getter; +use crate::reader::helpers::{read_f32, read_f64, read_i32, read_i64, read_string}; use crate::reader::structs::State; use crate::reader::user::common::UserInfo; use crate::reader::user::stable::offset::USER_PROFILE_OFFSET; use crate::Error; use rosu_mem::process::{Process, ProcessTraits}; -pub fn get_user_profile_base(p: &Process, state: &mut State) -> Result { +pub fn user_base(p: &Process, state: &mut State) -> Result { Ok(p.read_i32(p.read_i32(state.addresses.user_profile + USER_PROFILE_OFFSET.ptr)?)?) } - -pub fn get_user_id(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.id)?) -} - -pub fn get_user_bancho_status(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.bancho_status)?) -} - -pub fn get_user_country_code(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.country_code)?) -} - -pub fn get_user_username(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - let username_ptr = p.read_i32(user_profile_base + USER_PROFILE_OFFSET.username)?; - Ok(p.read_string(username_ptr)?) -} - -pub fn get_user_pp(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.pp)?) -} - -pub fn get_user_rankedscore(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i64(user_profile_base + USER_PROFILE_OFFSET.rankedscore)?) -} - -pub fn get_user_level(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_f32(user_profile_base + USER_PROFILE_OFFSET.level)?) -} - -pub fn get_user_playcount(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.playcount)?) -} - -pub fn get_user_rank(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.rank)?) -} - -pub fn get_user_playmode(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.playmode)?) -} - -pub fn get_user_accuracy(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; - Ok(p.read_f64(user_profile_base + USER_PROFILE_OFFSET.accuracy)?) +generate_offset_getter! { + id: i32 = read_i32(USER_PROFILE_OFFSET.id, user_base); + bancho_status: i32 = read_i32(USER_PROFILE_OFFSET.bancho_status, user_base); + country_code: i32 = read_i32(USER_PROFILE_OFFSET.country_code, user_base); + username: String = read_string(USER_PROFILE_OFFSET.username, user_base); + pp: i32 = read_i32(USER_PROFILE_OFFSET.pp, user_base); + rankedscore: i64 = read_i64(USER_PROFILE_OFFSET.rankedscore, user_base); + level: f32 = read_f32(USER_PROFILE_OFFSET.level, user_base); + playcount: i32 = read_i32(USER_PROFILE_OFFSET.playcount, user_base); + rank: i32 = read_i32(USER_PROFILE_OFFSET.rank, user_base); + playmode: i32 = read_i32(USER_PROFILE_OFFSET.playmode, user_base); + accuracy: f64 = read_f64(USER_PROFILE_OFFSET.accuracy, user_base); } -pub fn get_user_info(p: &Process, state: &mut State) -> Result { - let user_profile_base = get_user_profile_base(p, state)?; +pub fn info(p: &Process, state: &mut State) -> Result { + let user_profile_base = user_base(p, state)?; let user_profile = UserInfo { id: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.id)?, - username: p.read_string(p.read_i32(user_profile_base + USER_PROFILE_OFFSET.username)?)?, // TODO: need a fix idk how it show weirdly + username: p.read_string(user_profile_base + USER_PROFILE_OFFSET.username)?, pp: p.read_i32(user_profile_base + USER_PROFILE_OFFSET.pp)?, rankedscore: p.read_i64(user_profile_base + USER_PROFILE_OFFSET.rankedscore)?, level: p.read_f32(user_profile_base + USER_PROFILE_OFFSET.level)?,