rosu_memory_lib/reader/
helpers.rs

1use crate::reader::common::GameMode;
2use crate::reader::structs::Hit;
3use crate::reader::structs::State;
4use crate::Error;
5use rosu_mem::process::{Process, ProcessTraits};
6
7
8/// Generates standardized memory reading functions.
9/// 
10/// This macro creates functions that read specific data types from process memory
11/// using a base address and offset common usage for base types to not do getbaseaddr everytime.
12/// 
13/// # Syntax
14/// 
15/// ```rust
16/// generate_reader_fn! {
17///     function_name, return_type, read_method
18/// }
19/// ```
20/// 
21/// # Arguments
22/// 
23/// * `function_name` - Name of the generated function most likely read_<type>
24/// * `return_type` - Rust type to return (e.g., `i32`, `String`, `f64`)
25/// * `read_method` - Method name on Process trait (e.g., `read_i32`, `read_string`) most of the time from rosu_mem
26/// 
27/// # Examples
28/// 
29/// ```rust
30/// generate_reader_fn! {
31///     read_score, i32, read_i32
32/// }
33/// 
34/// ```
35macro_rules! generate_reader_fn {
36    (
37        $name:ident, $ret_ty:ty, $read_fn:ident
38    ) => {
39        pub(crate) fn $name(
40            p: &Process,
41            state: &mut State,
42            offset: i32,
43            get_base_addr: fn(&Process, &mut State) -> Result<i32, Error>,
44        ) -> Result<$ret_ty, Error> {
45            let base_addr = get_base_addr(p, state)?;
46            Ok(p.$read_fn(base_addr + offset)?)
47        }
48    };
49}
50
51/// Generates offset-based getter functions for memory access.
52/// 
53/// This macro creates functions that read data from specific memory offsets
54/// using a base address getter function.
55/// 
56/// # Syntax
57/// 
58/// ```rust
59/// generate_offset_getter! {
60///     function_name: return_type = read_method(offset, base_getter);
61///     another_function: another_type = another_read_method(another_offset, another_base);
62/// }
63/// ```
64/// 
65/// # Arguments
66/// 
67/// Each getter definition consists of:
68/// * `function_name` - Name of the generated function
69/// * `return_type` - Rust type to return
70/// * `read_method` - Memory reading method to use
71/// * `offset` - Memory offset from base address
72/// * `base_getter` - Function that returns the base address (most of the time from generate_reader_fn)
73/// 
74/// # Examples
75/// 
76/// ```rust
77/// generate_offset_getter! {
78///     score: i32 = read_i32(0x10, score_base),
79///     combo: i16 = read_i16(0x14, score_base),
80///     username: String = read_string(0x20, score_base),
81///     hp: f64 = read_f64(0x30, hp_base),
82/// }
83/// 
84/// // Generates functions like:
85/// // pub fn score(p: &Process, state: &mut State) -> Result<i32, Error> {
86/// //     Ok(<i32>::from(read_i32(p, state, 0x10, score_base)?))
87/// // }
88/// ```
89/// 
90/// # Generated Functions
91/// 
92/// Each definition generates a function with signature:
93/// ```rust
94/// pub fn function_name(p: &Process, state: &mut State) -> Result<return_type, Error>
95/// ```
96/// 
97/// # Memory Safety
98/// 
99/// The generated functions assume the offsets are valid and the base address
100/// getter returns a valid memory address.
101#[macro_export]
102macro_rules! generate_offset_getter {
103    (
104        $( $fn_name:ident : $ret_ty:ty = $read_fn:ident ( $offset:expr , $get_base:ident ); )*
105    ) => {
106        $(
107            pub fn $fn_name(p: &Process, state: &mut State) -> Result<$ret_ty, Error> {
108                Ok(<$ret_ty>::from($read_fn(p, state, $offset, $get_base)?))
109            }
110        )*
111    };
112}
113
114/// Generates OSU memory accessor methods with automatic client dispatching.
115/// 
116/// This macro eliminates boilerplate code by automatically generating
117/// client-specific accessor methods that handle different OSU client types
118/// (Stable, Lazer) with consistent error handling.
119/// 
120/// # Syntax
121/// 
122/// ```rust
123/// impl_osu_accessor! {
124///     fn method_name() -> return_type => implementation_path,
125///     fn another_method() -> another_type => another_implementation,
126/// }
127/// ```
128/// 
129/// # Arguments
130/// 
131/// * `method_name` - Name of the generated method (e.g., `game_state`, `score`)
132/// * `return_type` - Rust type to return (e.g., `GameState`, `i32`, `String`)
133/// * `implementation_path` - Path to the actual implementation (e.g., `stable::memory::game_state`)
134/// 
135/// # Examples
136/// 
137/// ```rust
138/// impl<'a> CommonReader<'a> {
139///     impl_osu_accessor! {
140///         fn game_state() -> GameState => stable::memory::game_state,
141///         fn menu_game_mode() -> GameMode => stable::memory::menu_game_mode,
142///         fn path_folder() -> PathBuf => stable::memory::path_folder,
143///     }
144/// }
145/// ```
146/// 
147/// # Generated Methods
148/// 
149/// For each definition, generates a method like:
150/// ```rust
151/// pub fn method_name(&mut self) -> Result<return_type, Error> {
152///     match self.osu_type {
153///         OsuClientKind::Stable => implementation_path(self.process, self.state),
154///         _ => Err(Error::Unsupported("Unsupported osu type for now".to_string())),
155///     }
156/// }
157/// ```
158/// 
159/// # Benefits
160/// 
161/// * **Reduces code duplication** - ~1000+ lines eliminated across the project
162/// * **Consistent API** - All accessors follow the same pattern
163/// * **Type safety** - Compile-time guarantees for return types
164/// * **Future-proof** - Easy to add new client types
165/// * **Maintainable** - Changes only need to be made in one place
166/// 
167/// # Errors
168/// 
169/// Generated methods return `Error::Unsupported` for client types that don't
170/// have implementations yet (currently only Stable is supported).
171/// 
172/// # Performance
173/// 
174/// No runtime overhead - all dispatching is done at compile time.
175#[macro_export]
176macro_rules! impl_osu_accessor {
177    ($(fn $name:ident() -> $ret:ty => $call:path),* $(,)?) => {
178        $(
179            pub fn $name(&mut self) -> Result<$ret, Error> {
180                match self.osu_type {
181                    OsuClientKind::Stable => $call(self.process, self.state),
182                    _ => Err(Error::Unsupported(
183                        "Unsupported osu type for now".to_string(),
184                    )),
185                }
186            }
187        )*
188    };
189}
190
191// TODO : idk where to put this
192#[inline]
193pub fn calculate_accuracy(gamemode: &GameMode, hit: &Hit) -> Result<f64, Error> {
194    let acc = match gamemode {
195        GameMode::Osu => {
196            let total = (hit._300 + hit._100 + hit._50 + hit._miss) as f64;
197            if total == 0.0 {
198                return Ok(0.0);
199            }
200            let score = hit._300 as f64 * 6.0 + hit._100 as f64 * 2.0 + hit._50 as f64;
201            (score / (total * 6.0)) * 100.0
202        }
203        GameMode::Taiko => {
204            let total = (hit._300 + hit._100 + hit._50 + hit._miss) as f64;
205            if total == 0.0 {
206                return Ok(0.0);
207            }
208            let score = hit._300 as f64 * 2.0 + hit._100 as f64;
209            (score / (total * 2.0)) * 100.0
210        }
211        GameMode::Catch => {
212            let caught = (hit._300 + hit._100 + hit._50) as f64;
213            let total = (hit._300 + hit._100 + hit._50 + hit._katu + hit._miss) as f64;
214            if total == 0.0 {
215                return Ok(0.0);
216            }
217            (caught / total) * 100.0
218        }
219        GameMode::Mania => {
220            let total = (hit._geki + hit._300 + hit._katu + hit._100 + hit._50 + hit._miss) as f64;
221            if total == 0.0 {
222                return Ok(0.0);
223            }
224            let score = (hit._geki + hit._300) as f64 * 6.0
225                + hit._katu as f64 * 4.0
226                + hit._100 as f64 * 2.0
227                + hit._50 as f64;
228            (score / (total * 6.0)) * 100.0
229        }
230        _ => return Ok(0.0),
231    };
232
233    Ok(acc)
234}
235
236
237
238// Macro usage
239generate_reader_fn!(read_string, String, read_string);
240generate_reader_fn!(read_i16, i16, read_i16);
241generate_reader_fn!(read_i32, i32, read_i32);
242generate_reader_fn!(read_u32, u32, read_u32);
243generate_reader_fn!(read_i64, i64, read_i64);
244generate_reader_fn!(read_u64, u64, read_u64);
245generate_reader_fn!(read_f32, f32, read_f32);
246generate_reader_fn!(read_f64, f64, read_f64);