1use rosu_pp::Beatmap;
2use rosu_memory_lib::init_loop;
3use rosu_memory_lib::reader::beatmap::stable::file::get_beatmap_path;
4use rosu_pp::{Difficulty, Performance};
5use rosu_memory_lib::reader::common::stable::memory::get_menu_mods;
6use rosu_mods::GameModsLegacy;
7use std::time::Duration;
8use eyre::Result;
9use rosu_mem::process::{Process};
10use rosu_memory_lib::reader::structs::State;
11
12struct CalculatorState {
27 current_pp: f64,
28 current_mods: i32,
29 current_beatmap: Beatmap,
30 current_beatmap_path: String,
31}
32
33impl CalculatorState {
34 fn new() -> Self {
36 Self {
37 current_pp: 0.0,
38 current_mods: 0,
39 current_beatmap: Beatmap::default(),
40 current_beatmap_path: String::new(),
41 }
42 }
43
44 fn update_mods(&mut self, new_mods: i32) -> bool {
46 if new_mods != self.current_mods {
47 self.current_mods = new_mods;
48
49 let mods_readable = GameModsLegacy::from_bits(self.current_mods as u32).to_string();
51 println!("Mods: {mods_readable}");
52 true
53 } else {
54 false
55 }
56 }
57
58 fn update_beatmap(&mut self, new_path: String) -> Result<bool> {
60 if new_path != self.current_beatmap_path {
61 println!("Loading new beatmap: {new_path}");
62
63 let beatmap = Beatmap::from_path(&new_path)?;
65 if let Err(suspicion) = beatmap.check_suspicion() {
66 eprintln!("Warning: Suspicious beatmap detected: {suspicion:?}");
67 return Ok(false);
68 }
69
70 self.current_beatmap = beatmap;
72 self.current_beatmap_path = new_path;
73 println!("Beatmap loaded successfully!");
74 Ok(true)
75 } else {
76 Ok(false)
77 }
78 }
79
80 fn update_pp(&mut self) {
82 let diff_attrs = Difficulty::new()
83 .mods(self.current_mods as u32)
84 .calculate(&self.current_beatmap);
85
86 let new_pp = Performance::new(diff_attrs).calculate().pp();
87 if (new_pp - self.current_pp).abs() > f64::EPSILON {
88 self.current_pp = new_pp;
89 println!("PP for current beatmap: {:.2}", self.current_pp);
90 }
91 }
92}
93
94fn process_game_state(process: &Process, state: &mut State, calc_state: &mut CalculatorState) -> Result<()> {
96 let mut mods_changed = false;
97 match get_beatmap_path(process, state) {
98 Ok(beatmap_path) => {
99 if let Ok(new_mods) = get_menu_mods(process, state) {
101 mods_changed = calc_state.update_mods(new_mods);
102 }
103
104 if calc_state.update_beatmap(beatmap_path)? && mods_changed {
106 calc_state.update_pp();
107 }
108 }
109 Err(e) => {
110 eprintln!("Failed to read beatmap path: {e}");
111 }
112 }
113 Ok(())
114}
115
116fn main() -> Result<()> {
117 let (mut state, process) = init_loop(500)?;
119 println!("Successfully connected to osu! process!");
120
121 let mut calc_state = CalculatorState::new();
123
124 loop {
126 if let Err(e) = process_game_state(&process, &mut state, &mut calc_state) {
127 eprintln!("Error during processing: {e}");
128 }
129
130 std::thread::sleep(Duration::from_millis(1000));
132 }
133}