rosu_memory_lib/reader/beatmap/stable/
file.rs1use crate::common::GameMode;
2use crate::reader::beatmap::common::{
3 BeatmapInfo, BeatmapLocation, BeatmapMetadata, BeatmapStarRating, BeatmapStats, BeatmapStatus,
4 BeatmapTechnicalInfo,
5};
6use crate::reader::beatmap::stable::location::{get_audio, get_filename, get_folder};
7use crate::reader::beatmap::stable::{get_beatmap_addr, offset::BEATMAP_OFFSET};
8use crate::reader::common::stable::memory::get_path_folder;
9use crate::reader::structs::State;
10use crate::Error;
11use rosu_map::section::hit_objects::HitObjectKind;
12use rosu_map::Beatmap as RmBeatmap;
13use rosu_mem::process::{Process, ProcessTraits};
14
15pub fn get_beatmap_path(p: &Process, state: &mut State) -> Result<String, Error> {
16 let folder = get_folder(p, state)?;
17 let filename = get_filename(p, state)?;
18 let song_path = get_path_folder(p, state)?;
19 Ok(format!("{song_path}/{folder}/{filename}"))
20}
21
22pub fn get_audio_path(p: &Process, state: &mut State) -> Result<String, Error> {
23 let folder = get_folder(p, state)?;
24 let audio = get_audio(p, state)?;
25 let song_path = get_path_folder(p, state)?;
26 Ok(format!("{song_path}/{folder}/{audio}"))
27}
28
29pub fn get_beatmap_md5(p: &Process, state: &mut State) -> Result<String, Error> {
30 crate::reader::beatmap::stable::memory::get_beatmap_md5(p, state)
32}
33
34pub fn get_beatmap_id(p: &Process, state: &mut State) -> Result<i32, Error> {
35 let path = get_beatmap_path(p, state)?;
36 let b = RmBeatmap::from_path(path)?;
37 Ok(b.beatmap_id)
38}
39
40pub fn get_beatmap_set_id(p: &Process, state: &mut State) -> Result<i32, Error> {
41 let path = get_beatmap_path(p, state)?;
42 let b = RmBeatmap::from_path(path)?;
43 Ok(b.beatmap_set_id)
44}
45
46pub fn get_beatmap_mode(p: &Process, state: &mut State) -> Result<GameMode, Error> {
47 let path = get_beatmap_path(p, state)?;
48 let b = RmBeatmap::from_path(path)?;
49 Ok(GameMode::from(b.mode as u32))
50}
51
52pub fn get_beatmap_tags(p: &Process, state: &mut State) -> Result<String, Error> {
53 let path = get_beatmap_path(p, state)?;
54 let b = RmBeatmap::from_path(path)?;
55 Ok(b.tags)
56}
57
58pub fn get_beatmap_length(p: &Process, state: &mut State) -> Result<i32, Error> {
59 crate::reader::beatmap::stable::memory::get_beatmap_length(p, state)
61}
62
63pub fn get_beatmap_drain_time(p: &Process, state: &mut State) -> Result<i32, Error> {
64 let path = get_beatmap_path(p, state)?;
65 let b = RmBeatmap::from_path(path)?;
66 let drain_time =
67 b.hit_objects.last().unwrap().start_time - b.hit_objects.first().unwrap().start_time;
68 Ok(drain_time as i32)
69}
70
71pub fn get_beatmap_status(p: &Process, state: &mut State) -> Result<BeatmapStatus, Error> {
72 crate::reader::beatmap::stable::memory::get_beatmap_status(p, state)
74}
75
76pub fn get_author(p: &Process, state: &mut State) -> Result<String, Error> {
77 let path = get_beatmap_path(p, state)?;
78 let b = RmBeatmap::from_path(path)?;
79 Ok(b.artist)
80}
81
82pub fn get_creator(p: &Process, state: &mut State) -> Result<String, Error> {
83 let path = get_beatmap_path(p, state)?;
84 let b = RmBeatmap::from_path(path)?;
85 Ok(b.creator)
86}
87
88pub fn get_title_romanized(p: &Process, state: &mut State) -> Result<String, Error> {
89 let path = get_beatmap_path(p, state)?;
90 let b = RmBeatmap::from_path(path)?;
91 Ok(b.title)
92}
93
94pub fn get_title_original(p: &Process, state: &mut State) -> Result<String, Error> {
95 let path = get_beatmap_path(p, state)?;
96 let b = RmBeatmap::from_path(path)?;
97 Ok(b.title_unicode)
98}
99
100pub fn get_difficulty(p: &Process, state: &mut State) -> Result<String, Error> {
101 let path = get_beatmap_path(p, state)?;
102 let b = RmBeatmap::from_path(path)?;
103 Ok(b.version)
104}
105
106pub fn get_beatmap_od(p: &Process, state: &mut State) -> Result<f32, Error> {
107 let path = get_beatmap_path(p, state)?;
108 let b = RmBeatmap::from_path(path)?;
109 Ok(b.overall_difficulty)
110}
111
112pub fn get_beatmap_ar(p: &Process, state: &mut State) -> Result<f32, Error> {
113 let path = get_beatmap_path(p, state)?;
114 let b = RmBeatmap::from_path(path)?;
115 Ok(b.approach_rate)
116}
117
118pub fn get_beatmap_cs(p: &Process, state: &mut State) -> Result<f32, Error> {
119 let path = get_beatmap_path(p, state)?;
120 let b = RmBeatmap::from_path(path)?;
121 Ok(b.circle_size)
122}
123
124pub fn get_beatmap_hp(p: &Process, state: &mut State) -> Result<f32, Error> {
125 let path = get_beatmap_path(p, state)?;
126 let b = RmBeatmap::from_path(path)?;
127 Ok(b.hp_drain_rate)
128}
129
130pub fn get_beatmap_object_count(p: &Process, state: &mut State) -> Result<u32, Error> {
131 let path = get_beatmap_path(p, state)?;
132 let b = RmBeatmap::from_path(path)?;
133 Ok(b.hit_objects.len() as u32)
134}
135
136pub fn get_beatmap_slider_count(p: &Process, state: &mut State) -> Result<i32, Error> {
137 let path = get_beatmap_path(p, state)?;
138 let b = RmBeatmap::from_path(path)?;
139 Ok(b.hit_objects
140 .iter()
141 .filter(|h| matches!(h.kind, HitObjectKind::Slider(_)))
142 .count() as i32)
143}
144
145pub fn get_beatmap_star_rating(p: &Process, state: &mut State) -> Result<BeatmapStarRating, Error> {
146 let folder = get_folder(p, state)?;
147 let filename = get_filename(p, state)?;
148 let song_path = get_path_folder(p, state)?;
149 let path = format!("{song_path}/{folder}/{filename}");
150 let b = rosu_pp::Beatmap::from_path(path)?;
151 let diff_attrs = rosu_pp::Difficulty::new().calculate(&b);
152 let diff_dt = rosu_pp::Difficulty::new().mods(64).calculate(&b);
153 let diff_ht = rosu_pp::Difficulty::new().mods(256).calculate(&b);
154 let no_mod = diff_attrs.stars();
155 let dt = diff_dt.stars();
156 let ht = diff_ht.stars();
157 Ok(BeatmapStarRating { no_mod, dt, ht })
158}
159
160pub fn get_beatmap_stats(p: &Process, state: &mut State) -> Result<BeatmapStats, Error> {
161 let beatmap_addr = get_beatmap_path(p, state)?;
162 let b = RmBeatmap::from_path(beatmap_addr)?;
163 Ok(BeatmapStats {
164 ar: b.approach_rate,
165 od: b.overall_difficulty,
166 cs: b.circle_size,
167 hp: b.hp_drain_rate,
168 total_length: b.hit_objects.last().unwrap().start_time as i32
169 - b.hit_objects.first().unwrap().start_time as i32,
170 star_rating: get_beatmap_star_rating(p, state)?,
171 object_count: b.hit_objects.len() as i32,
172 slider_count: b
173 .hit_objects
174 .iter()
175 .filter(|h| matches!(h.kind, HitObjectKind::Slider(_)))
176 .count() as i32,
177 })
178}
179
180pub fn get_beatmap_info(p: &Process, state: &mut State) -> Result<BeatmapInfo, Error> {
181 let beatmap_file = get_beatmap_path(p, state)?;
182 let beatmap_addr = get_beatmap_addr(p, state)?;
183 let b = RmBeatmap::from_path(beatmap_file)?;
184 Ok(BeatmapInfo {
186 technical: BeatmapTechnicalInfo {
187 md5: crate::reader::beatmap::stable::file::get_beatmap_md5(p, state)?,
188 id: b.beatmap_id,
189 set_id: b.beatmap_set_id,
190 mode: GameMode::Osu,
191 ranked_status: crate::reader::beatmap::stable::file::get_beatmap_status(p, state)?,
192 },
193 metadata: BeatmapMetadata {
194 author: b.artist,
195 creator: b.creator,
196 title_romanized: b.title,
197 title_original: b.title_unicode,
198 difficulty: b.version,
199 tags: b.tags,
200 },
201 stats: BeatmapStats {
202 ar: b.approach_rate,
203 od: b.overall_difficulty,
204 cs: b.circle_size,
205 hp: b.hp_drain_rate,
206 total_length: b.hit_objects.last().unwrap().start_time as i32
207 - b.hit_objects.first().unwrap().start_time as i32,
208 star_rating: get_beatmap_star_rating(p, state)?,
209 object_count: b.hit_objects.len() as i32,
210 slider_count: b
211 .hit_objects
212 .iter()
213 .filter(|h| matches!(h.kind, HitObjectKind::Slider(_)))
214 .count() as i32,
215 },
216 location: BeatmapLocation {
217 folder: p.read_string(beatmap_addr + BEATMAP_OFFSET.location.folder)?,
218 filename: p.read_string(beatmap_addr + BEATMAP_OFFSET.location.filename)?,
219 audio: p.read_string(beatmap_addr + BEATMAP_OFFSET.location.audio)?,
220 cover: p.read_string(beatmap_addr + BEATMAP_OFFSET.location.cover)?,
221 },
222 })
223}