rosu_memory_lib/reader/beatmap/stable/
file.rs

1use 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    // TODO: implement this for now will get from memory
31    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    // implement this later for now will get from memory
60    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    // cant do this in file mode
73    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    // done like that to be more efficient reading the string one by one would need to reload addr everytime which cost more
185    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}