rosu_memory_lib/reader/resultscreen/stable/
memory.rs

1use crate::generate_offset_getter;
2use crate::reader::common::stable::memory::check_game_state;
3use crate::reader::common::GameMode;
4use crate::reader::common::GameState;
5use crate::reader::helpers::{calculate_accuracy, read_i16, read_i32, read_string};
6use crate::reader::resultscreen::common::ResultScreenInfo;
7use crate::reader::resultscreen::stable::offset::RESULT_SCREEN_OFFSET;
8use crate::reader::structs::{Hit, State};
9use crate::Error;
10use rosu_mem::process::{Process, ProcessTraits};
11
12pub fn result_screen_ptr(p: &Process, state: &mut State) -> Result<i32, Error> {
13    if check_game_state(p, state, GameState::ResultScreen)? {
14        Ok(p.read_i32(state.addresses.rulesets - RESULT_SCREEN_OFFSET.ptr)?)
15    } else {
16        Err(Error::NotAvailable("Not in ResultScreen".to_string()))
17    }
18}
19
20pub fn hits(p: &Process, state: &mut State) -> Result<Hit, Error> {
21    let score_base = result_screen_base(p, state)?;
22    // Read all hits data in one memory operation
23    let mut hits_buffer = [0u8; size_of::<i16>() * 6];
24    p.read(
25        score_base + RESULT_SCREEN_OFFSET.hits._100,
26        size_of::<i16>() * 6,
27        &mut hits_buffer,
28    )?;
29
30    // Safety: unwrap here because buffer is already initialized and filled
31    // with zeros, the worst case scenario is hits going to be zeros
32    Ok(Hit {
33        _100: i16::from_le_bytes(hits_buffer[0..2].try_into().unwrap()),
34        _300: i16::from_le_bytes(hits_buffer[2..4].try_into().unwrap()),
35        _50: i16::from_le_bytes(hits_buffer[4..6].try_into().unwrap()),
36        _geki: i16::from_le_bytes(hits_buffer[6..8].try_into().unwrap()),
37        _katu: i16::from_le_bytes(hits_buffer[8..10].try_into().unwrap()),
38        _miss: i16::from_le_bytes(hits_buffer[10..12].try_into().unwrap()),
39    })
40}
41
42pub fn accuracy(p: &Process, state: &mut State) -> Result<f64, Error> {
43    calculate_accuracy(&mode(p, state)?, &hits(p, state)?)
44}
45
46generate_offset_getter! {
47    result_screen_addr: i32 = read_i32(RESULT_SCREEN_OFFSET.addr, result_screen_ptr);
48    result_screen_base: i32 = read_i32(RESULT_SCREEN_OFFSET.base, result_screen_addr);
49    username: String = read_string(RESULT_SCREEN_OFFSET.username, result_screen_base);
50    score: i32 = read_i32(RESULT_SCREEN_OFFSET.score, result_screen_base);
51    max_combo: i16 = read_i16(RESULT_SCREEN_OFFSET.max_combo, result_screen_base);
52    mode: GameMode = read_i32(RESULT_SCREEN_OFFSET.mode, result_screen_base);
53    hits_300: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._300, result_screen_base);
54    hits_100: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._100, result_screen_base);
55    hits_50: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._50, result_screen_base);
56    hits_miss: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._miss, result_screen_base);
57    hits_geki: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._geki, result_screen_base);
58    hits_katu: i16 = read_i16(RESULT_SCREEN_OFFSET.hits._katu, result_screen_base);
59}
60
61pub fn info(p: &Process, state: &mut State) -> Result<ResultScreenInfo, Error> {
62    let hits = hits(p, state)?;
63    let mode = mode(p, state)?;
64    let accuracy = calculate_accuracy(&mode, &hits)?;
65    let base = result_screen_base(p, state)?;
66    Ok(ResultScreenInfo {
67        username: p.read_string(base + RESULT_SCREEN_OFFSET.username)?,
68        mode,
69        max_combo: p.read_i16(base + RESULT_SCREEN_OFFSET.max_combo)?,
70        score: p.read_i32(base + RESULT_SCREEN_OFFSET.score)?,
71        hits,
72        accuracy,
73    })
74}