add osu memory reader
This commit is contained in:
parent
d751ff4505
commit
80a4b9f1d1
227
src-tauri/Cargo.lock
generated
227
src-tauri/Cargo.lock
generated
@ -1097,12 +1097,26 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ezpplauncher"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"eyre",
|
||||
"rosu-mem",
|
||||
"rosu-pp",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-dialog",
|
||||
@ -1110,6 +1124,7 @@ dependencies = [
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-single-instance",
|
||||
"tauri-plugin-sql",
|
||||
"tracy-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1415,6 +1430,19 @@ dependencies = [
|
||||
"x11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@ -1953,6 +1981,12 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@ -2235,6 +2269,19 @@ version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "loom"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"generator",
|
||||
"scoped-tls",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mac"
|
||||
version = "0.1.1"
|
||||
@ -2264,6 +2311,15 @@ dependencies = [
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.10"
|
||||
@ -2384,6 +2440,18 @@ version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
@ -2425,6 +2493,16 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.4"
|
||||
@ -2493,7 +2571,7 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
|
||||
dependencies = [
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
@ -2779,6 +2857,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "pango"
|
||||
version = "0.18.3"
|
||||
@ -3315,8 +3399,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3327,9 +3420,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
@ -3396,6 +3495,22 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosu-mem"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/486c/rosu-mem.git?tag=v1.0.0#2349cb845e3149c71873bfeda7ec81afec94c8c0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"nix 0.25.1",
|
||||
"paste",
|
||||
"windows 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosu-pp"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/486c/rosu-pp.git?branch=main#a82dd41e5008944893b2434901d62dd5e6160d74"
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.7"
|
||||
@ -3444,6 +3559,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
@ -3697,6 +3818,15 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_child"
|
||||
version = "1.0.1"
|
||||
@ -4204,7 +4334,7 @@ dependencies = [
|
||||
"tao-macros",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
@ -4274,7 +4404,7 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4469,7 +4599,7 @@ dependencies = [
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.6",
|
||||
"url",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4494,7 +4624,7 @@ dependencies = [
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"wry",
|
||||
]
|
||||
|
||||
@ -4615,6 +4745,16 @@ dependencies = [
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
@ -4807,6 +4947,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracy-client"
|
||||
version = "0.16.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "307e6b7030112fe9640fdd87988a40795549ba75c355f59485d14e6b444d2987"
|
||||
dependencies = [
|
||||
"loom",
|
||||
"once_cell",
|
||||
"tracy-client-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracy-client-sys"
|
||||
version = "0.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d104d610dfa9dd154535102cc9c6164ae1fa37842bc2d9e83f9ac82b0ae0882"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4991,6 +5181,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
@ -5268,7 +5464,7 @@ checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c"
|
||||
dependencies = [
|
||||
"webview2-com-macros",
|
||||
"webview2-com-sys",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
@ -5292,7 +5488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886"
|
||||
dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
]
|
||||
|
||||
@ -5351,6 +5547,15 @@ dependencies = [
|
||||
"windows-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.58.0"
|
||||
@ -5735,7 +5940,7 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
|
@ -18,10 +18,15 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
rosu-mem = { git = "https://github.com/486c/rosu-mem.git", tag = "v1.0.0" }
|
||||
rosu-pp = { git = "https://github.com/486c/rosu-pp.git", branch = "main", features = ["gradual"] }
|
||||
tracy-client = { version = "0.16.4", default-features = false }
|
||||
eyre = "0.6.12"
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-shell = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_repr = "0.1.17"
|
||||
tauri-plugin-sql = "2.2.0"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-fs = "2"
|
||||
|
@ -1,8 +1,23 @@
|
||||
mod reading_loop;
|
||||
mod structs;
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
use crate::reading_loop::process_reading_loop;
|
||||
use crate::structs::{State, StaticAddresses};
|
||||
use eyre::Report;
|
||||
use rosu_mem::{
|
||||
error::ProcessError,
|
||||
process::{Process, ProcessTraits},
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{thread, time::Duration};
|
||||
use structs::{InnerValues, OutputValues};
|
||||
use tauri::Manager;
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
println!("starting ezpplauncher!");
|
||||
osu_memory_reading();
|
||||
let mut builder = tauri::Builder::default().plugin(tauri_plugin_fs::init());
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
@ -21,3 +36,78 @@ pub fn run() {
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
fn osu_memory_reading() {
|
||||
thread::spawn(|| {
|
||||
let output_values = Arc::new(Mutex::new(OutputValues::default()));
|
||||
let inner_values = InnerValues::default();
|
||||
|
||||
let mut state = State {
|
||||
addresses: StaticAddresses::default(),
|
||||
ivalues: inner_values,
|
||||
values: output_values,
|
||||
};
|
||||
|
||||
'init_loop: loop {
|
||||
println!("Searching for osu! process");
|
||||
let p = match Process::initialize("osu!.exe") {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
println!("{:?}", Report::new(e));
|
||||
thread::sleep(Duration::from_millis(5000));
|
||||
continue 'init_loop;
|
||||
}
|
||||
};
|
||||
|
||||
println!("Reading static signatures...");
|
||||
match StaticAddresses::new(&p) {
|
||||
Ok(v) => state.addresses = v,
|
||||
Err(e) => {
|
||||
match e.downcast_ref::<ProcessError>() {
|
||||
Some(&ProcessError::ProcessNotFound) => {
|
||||
thread::sleep(Duration::from_millis(2000));
|
||||
continue 'init_loop
|
||||
},
|
||||
#[cfg(target_os = "windows")]
|
||||
Some(&ProcessError::OsError{ .. }) => {
|
||||
println!("{:?}", e);
|
||||
thread::sleep(Duration::from_millis(2000));
|
||||
continue 'init_loop
|
||||
},
|
||||
Some(_) | None => {
|
||||
println!("{:?}", e);
|
||||
thread::sleep(Duration::from_millis(2000));
|
||||
continue 'init_loop
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
println!("osu! process found, Starting reading loop");
|
||||
|
||||
'main_loop: loop {
|
||||
if let Err(e) = process_reading_loop(&p, &mut state) {
|
||||
match e.downcast_ref::<ProcessError>() {
|
||||
Some(&ProcessError::ProcessNotFound) => {
|
||||
thread::sleep(Duration::from_millis(5000));
|
||||
continue 'init_loop;
|
||||
},
|
||||
#[cfg(target_os = "windows")]
|
||||
Some(&ProcessError::OsError{ .. }) => {
|
||||
println!("{:?}", e);
|
||||
thread::sleep(Duration::from_millis(5000));
|
||||
continue 'init_loop
|
||||
},
|
||||
Some(_) | None => {
|
||||
println!("{:?}", e);
|
||||
thread::sleep(Duration::from_millis(5000));
|
||||
continue 'main_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{:?}", state.values.clone());
|
||||
thread::sleep(Duration::from_millis(2000));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
462
src-tauri/src/reading_loop.rs
Normal file
462
src-tauri/src/reading_loop.rs
Normal file
@ -0,0 +1,462 @@
|
||||
use std::{borrow::Cow, mem::size_of};
|
||||
|
||||
use rosu_pp::Beatmap;
|
||||
use tracy_client::*;
|
||||
use eyre::Result;
|
||||
|
||||
use rosu_mem::process::{Process, ProcessTraits};
|
||||
|
||||
use crate::structs::{State, GameState, BeatmapStatus, OutputValues};
|
||||
|
||||
|
||||
/// Here cases when key overlay is not gonna be available for reading:
|
||||
/// 1. Map is not fully loaded
|
||||
/// 2. If key overlay is not enabled in settings
|
||||
pub fn process_key_overlay(
|
||||
p: &Process,
|
||||
values: &mut OutputValues,
|
||||
ruleset_addr: i32,
|
||||
) -> Result<()> {
|
||||
let keyoverlay_ptr = p.read_i32(ruleset_addr + 0xB0)?;
|
||||
|
||||
if keyoverlay_ptr == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// TODO optimize using batches?
|
||||
|
||||
let keyoverlay_addr = p.read_i32(
|
||||
p.read_i32(keyoverlay_ptr + 0x10)? + 0x4
|
||||
)?;
|
||||
|
||||
values.keyoverlay.k1_pressed = p.read_i8(
|
||||
p.read_i32(keyoverlay_addr + 0x8)? + 0x1C
|
||||
)? != 0;
|
||||
|
||||
values.keyoverlay.k1_count = p.read_i32(
|
||||
p.read_i32(keyoverlay_addr + 0x8)? + 0x14
|
||||
)? as u32;
|
||||
|
||||
values.keyoverlay.k2_pressed = p.read_i8(
|
||||
p.read_i32(keyoverlay_addr + 0xC)? + 0x1C
|
||||
)? != 0;
|
||||
|
||||
values.keyoverlay.k2_count = p.read_i32(
|
||||
p.read_i32(keyoverlay_addr + 0xC)? + 0x14
|
||||
)? as u32;
|
||||
|
||||
values.keyoverlay.m1_pressed = p.read_i8(
|
||||
p.read_i32(keyoverlay_addr + 0x10)? + 0x1C
|
||||
)? != 0;
|
||||
|
||||
values.keyoverlay.m1_count = p.read_i32(
|
||||
p.read_i32(keyoverlay_addr + 0x10)? + 0x14
|
||||
)? as u32;
|
||||
|
||||
values.keyoverlay.m2_pressed = p.read_i8(
|
||||
p.read_i32(keyoverlay_addr + 0x14)? + 0x1C
|
||||
)? != 0;
|
||||
|
||||
values.keyoverlay.m2_count = p.read_i32(
|
||||
p.read_i32(keyoverlay_addr + 0x14)? + 0x14
|
||||
)? as u32;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_gameplay(
|
||||
p: &Process,
|
||||
state: &mut State,
|
||||
values: &mut OutputValues,
|
||||
ruleset_addr: i32,
|
||||
) -> Result<()> {
|
||||
let _span = span!("Gameplay data");
|
||||
|
||||
if values.prev_playtime > values.playtime {
|
||||
values.reset_gameplay();
|
||||
state.ivalues.reset();
|
||||
}
|
||||
|
||||
values.prev_playtime = values.playtime;
|
||||
|
||||
if ruleset_addr == 0 {
|
||||
return Ok(())
|
||||
};
|
||||
|
||||
let gameplay_base =
|
||||
p.read_i32(ruleset_addr + 0x68)?;
|
||||
|
||||
if gameplay_base == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let score_base = p.read_i32(gameplay_base + 0x38)?;
|
||||
|
||||
let hp_base = p.read_i32(gameplay_base + 0x40)?;
|
||||
|
||||
// Random value but seems to work pretty well
|
||||
// TODO sometimes playtime is >150 but game doesn't have
|
||||
// values yet unreal to debug, occurs rarely and randomly
|
||||
if values.playtime > 150 {
|
||||
values.gameplay.current_hp = p.read_f64(hp_base + 0x1C)?;
|
||||
values.gameplay.current_hp_smooth =
|
||||
p.read_f64(hp_base + 0x14)?;
|
||||
}
|
||||
|
||||
let hit_errors_base = p.read_i32(score_base + 0x38)?;
|
||||
|
||||
p.read_i32_array(
|
||||
hit_errors_base,
|
||||
&mut values.gameplay.hit_errors
|
||||
)?;
|
||||
|
||||
values.gameplay.unstable_rate =
|
||||
values.gameplay.calculate_unstable_rate();
|
||||
|
||||
// TODO batch
|
||||
values.gameplay.mode = p.read_i32(score_base + 0x64)?;
|
||||
|
||||
// TODO batch
|
||||
values.gameplay.hit_300 = p.read_i16(score_base + 0x8a)?;
|
||||
values.gameplay.hit_100 = p.read_i16(score_base + 0x88)?;
|
||||
values.gameplay.hit_50 = p.read_i16(score_base + 0x8c)?;
|
||||
|
||||
values.gameplay.username = p.read_string(score_base + 0x28)?;
|
||||
|
||||
// TODO batch
|
||||
values.gameplay.hit_geki = p.read_i16(score_base + 0x8e)?;
|
||||
values.gameplay.hit_katu = p.read_i16(score_base + 0x90)?;
|
||||
values.gameplay.hit_miss = p.read_i16(score_base + 0x92)?;
|
||||
|
||||
let passed_objects = values.gameplay.passed_objects()?;
|
||||
|
||||
values.gameplay.passed_objects = passed_objects;
|
||||
|
||||
values.gameplay.update_accuracy();
|
||||
|
||||
values.gameplay.score = p.read_i32(score_base + 0x78)?;
|
||||
|
||||
values.gameplay.combo = p.read_i16(score_base + 0x94)?;
|
||||
values.gameplay.max_combo = p.read_i16(score_base + 0x68)?;
|
||||
|
||||
if values.gameplay.combo < values.prev_combo
|
||||
&& values.gameplay.hit_miss == values.prev_hit_miss {
|
||||
values.gameplay.slider_breaks += 1;
|
||||
}
|
||||
|
||||
values.prev_hit_miss = values.gameplay.hit_miss;
|
||||
|
||||
let mods_xor_base = p.read_i32(score_base + 0x1C)?;
|
||||
|
||||
let mods_raw = p.read_u64(mods_xor_base + 0x8)?;
|
||||
|
||||
let mods_xor1 = mods_raw & 0xFFFFFFFF;
|
||||
let mods_xor2 = mods_raw >> 32;
|
||||
|
||||
// Read key overlay
|
||||
process_key_overlay(
|
||||
p,
|
||||
values,
|
||||
ruleset_addr
|
||||
)?;
|
||||
|
||||
values.gameplay.mods = (mods_xor1 ^ mods_xor2) as u32;
|
||||
values.update_readable_mods();
|
||||
|
||||
// Calculate pp
|
||||
values.update_current_pp(&mut state.ivalues);
|
||||
values.update_fc_pp(&mut state.ivalues);
|
||||
|
||||
values.prev_passed_objects = passed_objects;
|
||||
values.prev_combo = values.gameplay.combo;
|
||||
|
||||
values.gameplay.grade = values.gameplay.get_current_grade();
|
||||
values.update_current_bpm();
|
||||
values.update_kiai();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_reading_loop(
|
||||
p: &Process,
|
||||
state: &mut State
|
||||
) -> Result<()> {
|
||||
let _span = span!("reading loop");
|
||||
|
||||
let values = state.values.clone();
|
||||
let mut values = values.lock().unwrap();
|
||||
|
||||
let menu_mods_ptr = p.read_i32(
|
||||
state.addresses.menu_mods + 0x9
|
||||
)?;
|
||||
|
||||
let menu_mods = p.read_u32(menu_mods_ptr)?;
|
||||
values.menu_mods = menu_mods;
|
||||
|
||||
let playtime_ptr = p.read_i32(state.addresses.playtime + 0x5)?;
|
||||
values.playtime = p.read_i32(playtime_ptr)?;
|
||||
|
||||
let beatmap_ptr = p.read_i32(state.addresses.base - 0xC)?;
|
||||
let beatmap_addr = p.read_i32(beatmap_ptr)?;
|
||||
|
||||
let status_ptr = p.read_i32(state.addresses.status - 0x4)?;
|
||||
|
||||
let skin_ptr = p.read_i32(state.addresses.skin + 0x4)?;
|
||||
let skin_data = p.read_i32(skin_ptr)?;
|
||||
values.skin = p.read_string(skin_data + 0x44)?;
|
||||
|
||||
values.state = GameState::from(
|
||||
p.read_u32(status_ptr)?
|
||||
);
|
||||
|
||||
// Handle leaving `Playing` state
|
||||
if values.prev_state == GameState::Playing
|
||||
&& values.state != GameState::Playing {
|
||||
values.reset_gameplay();
|
||||
state.ivalues.reset();
|
||||
values.update_stars_and_ss_pp();
|
||||
}
|
||||
|
||||
if beatmap_addr == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
if values.state != GameState::MultiplayerLobby {
|
||||
let mut beatmap_stats_buff = [0u8; size_of::<f32>() * 4];
|
||||
|
||||
p.read(
|
||||
beatmap_addr + 0x2c,
|
||||
size_of::<f32>() * 4,
|
||||
&mut beatmap_stats_buff
|
||||
)?;
|
||||
|
||||
// Safety: unwrap here because buff is already initialized
|
||||
// and filled with zeros, the worst case scenario is
|
||||
// ar, cs, od, hp going to be zero's
|
||||
values.beatmap.ar = f32::from_le_bytes(
|
||||
beatmap_stats_buff[0..4].try_into().unwrap()
|
||||
);
|
||||
|
||||
values.beatmap.cs = f32::from_le_bytes(
|
||||
beatmap_stats_buff[4..8].try_into().unwrap()
|
||||
);
|
||||
|
||||
values.beatmap.hp = f32::from_le_bytes(
|
||||
beatmap_stats_buff[8..12].try_into().unwrap()
|
||||
);
|
||||
|
||||
values.beatmap.od = f32::from_le_bytes(
|
||||
beatmap_stats_buff[12..].try_into().unwrap()
|
||||
);
|
||||
|
||||
let plays_addr = p.read_i32(state.addresses.base - 0x33)? + 0xC;
|
||||
values.plays = p.read_i32(plays_addr)?;
|
||||
|
||||
values.beatmap.artist = p.read_string(beatmap_addr + 0x18)?;
|
||||
values.beatmap.title = p.read_string(beatmap_addr + 0x24)?;
|
||||
values.beatmap.creator = p.read_string(beatmap_addr + 0x7C)?;
|
||||
values.beatmap.difficulty = p.read_string(beatmap_addr + 0xAC)?;
|
||||
values.beatmap.map_id = p.read_i32(beatmap_addr + 0xC8)?; // TODO batch
|
||||
values.beatmap.mapset_id = p.read_i32(beatmap_addr + 0xCC)?; // TODO batch
|
||||
}
|
||||
|
||||
values.beatmap.beatmap_status = BeatmapStatus::from(
|
||||
p.read_i16(beatmap_addr + 0x12C)?
|
||||
);
|
||||
|
||||
let mut new_map = false;
|
||||
|
||||
// All time values that available everywhere
|
||||
values.chat_enabled = p.read_i8(
|
||||
state.addresses.chat_checker - 0x20
|
||||
)? != 0;
|
||||
|
||||
// Skin folder
|
||||
let skin_data_ptr = p.read_i32(
|
||||
p.read_i32(state.addresses.skin + 4)?
|
||||
)?;
|
||||
|
||||
values.skin_folder = p.read_string(
|
||||
skin_data_ptr + 68
|
||||
)?;
|
||||
|
||||
if values.state != GameState::PreSongSelect
|
||||
&& values.state != GameState::MultiplayerLobby
|
||||
&& values.state != GameState::MultiplayerResultScreen {
|
||||
let menu_mode_addr = p.read_i32(state.addresses.base - 0x33)?;
|
||||
|
||||
let beatmap_file = p.read_string(beatmap_addr + 0x90)?;
|
||||
let beatmap_folder = p.read_string(beatmap_addr + 0x78)?;
|
||||
let audio_file = p.read_string(beatmap_addr + 0x64)?;
|
||||
values.menu_mode = p.read_i32(menu_mode_addr)?;
|
||||
|
||||
values.beatmap.paths.beatmap_full_path
|
||||
= values.osu_path.join("Songs/");
|
||||
|
||||
values.beatmap.paths.beatmap_full_path.push(&beatmap_folder);
|
||||
values.beatmap.paths.beatmap_full_path.push(&beatmap_file);
|
||||
|
||||
values.beatmap.md5 =
|
||||
p.read_string(beatmap_addr + 0x6C)?;
|
||||
|
||||
// Check if beatmap changed
|
||||
if (beatmap_folder != values.beatmap.paths.beatmap_folder
|
||||
|| beatmap_file != values.beatmap.paths.beatmap_file
|
||||
|| values.prev_menu_mode != values.menu_mode)
|
||||
&& values.beatmap.paths.beatmap_full_path.exists() {
|
||||
let current_beatmap = match Beatmap::from_path(
|
||||
&values.beatmap.paths.beatmap_full_path
|
||||
) {
|
||||
Ok(beatmap) => {
|
||||
new_map = true;
|
||||
|
||||
values.beatmap.paths.background_file.clone_from(
|
||||
&beatmap.background.filename
|
||||
);
|
||||
|
||||
if let Some(hobj) = beatmap.hit_objects.last() {
|
||||
values.beatmap.last_obj_time = hobj.start_time;
|
||||
}
|
||||
|
||||
if let Some(hobj) = beatmap.hit_objects.first() {
|
||||
values.beatmap.first_obj_time = hobj.start_time;
|
||||
}
|
||||
|
||||
values.beatmap.bpm = beatmap.bpm();
|
||||
|
||||
Some(beatmap)
|
||||
},
|
||||
Err(_) => {
|
||||
println!("Failed to parse beatmap");
|
||||
None
|
||||
},
|
||||
};
|
||||
|
||||
values.current_beatmap = current_beatmap;
|
||||
|
||||
values.beatmap.paths.beatmap_folder = beatmap_folder;
|
||||
values.beatmap.paths.beatmap_file = beatmap_file;
|
||||
values.beatmap.paths.audio_file = audio_file;
|
||||
|
||||
values.update_min_max_bpm();
|
||||
values.update_full_paths();
|
||||
values.adjust_bpm();
|
||||
}
|
||||
}
|
||||
|
||||
// store the converted map so it's not converted
|
||||
// everytime it's used for pp calc
|
||||
if new_map {
|
||||
if let Some(map) = &values.current_beatmap {
|
||||
if let Cow::Owned(converted) = map
|
||||
.convert_mode(values.menu_gamemode())
|
||||
{
|
||||
values.current_beatmap = Some(converted);
|
||||
}
|
||||
}
|
||||
|
||||
values.update_stars_and_ss_pp();
|
||||
values.update_current_pp(&mut state.ivalues);
|
||||
}
|
||||
|
||||
let ruleset_addr = p.read_i32(
|
||||
p.read_i32(state.addresses.rulesets - 0xb)? + 0x4
|
||||
)?;
|
||||
|
||||
let audio_time_ptr = p.read_i32(state.addresses.audio_time_base + 0x9)?;
|
||||
values.precise_audio_time = p.read_i32(audio_time_ptr)?;
|
||||
|
||||
// If this happened there is zero sense to continue
|
||||
// reading because all the values depends on this
|
||||
// address
|
||||
if ruleset_addr == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// Process result screen
|
||||
// TODO handle situations when result screen is not ready
|
||||
if values.state == GameState::ResultScreen {
|
||||
let result_base = p.read_i32(ruleset_addr+ 0x38)?;
|
||||
|
||||
values.result_screen.username = p.read_string(result_base + 0x28)?;
|
||||
|
||||
let mods_xor_base = p.read_i32(result_base + 0x1C)?;
|
||||
|
||||
// TODO batch
|
||||
let mods_xor1 = p.read_i32(mods_xor_base + 0xC)?;
|
||||
let mods_xor2 = p.read_i32(mods_xor_base + 0x8)?;
|
||||
|
||||
values.result_screen.mods = (mods_xor1 ^ mods_xor2) as u32;
|
||||
values.result_screen.mode = p.read_i32(result_base + 0x64)? as u8;
|
||||
values.result_screen.score = p.read_i32(result_base + 0x78)?;
|
||||
|
||||
// TODO batch
|
||||
values.result_screen.hit_300 = p.read_i16(result_base + 0x8A)?;
|
||||
values.result_screen.hit_100 = p.read_i16(result_base + 0x88)?;
|
||||
values.result_screen.hit_50 = p.read_i16(result_base + 0x8C)?;
|
||||
values.result_screen.hit_geki = p.read_i16(result_base + 0x8E)?;
|
||||
values.result_screen.hit_katu = p.read_i16(result_base + 0x90)?;
|
||||
|
||||
values.result_screen.update_accuracy();
|
||||
}
|
||||
|
||||
// Process gameplay
|
||||
if values.state == GameState::Playing {
|
||||
let res = process_gameplay(
|
||||
p,
|
||||
state,
|
||||
&mut values,
|
||||
ruleset_addr
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
println!("{:?}", e);
|
||||
println!("Skipped gameplay reading, probably it's not ready yet");
|
||||
}
|
||||
}
|
||||
|
||||
// Handling entering `ResultScreen` state
|
||||
if values.prev_state != GameState::ResultScreen
|
||||
&& values.state == GameState::ResultScreen {
|
||||
if values.prev_state != GameState::Playing {
|
||||
values.update_current_pp(&mut state.ivalues);
|
||||
}
|
||||
|
||||
values.update_stars_and_ss_pp();
|
||||
}
|
||||
|
||||
// Handling entering `SongSelect` state
|
||||
if values.prev_state != GameState::SongSelect
|
||||
&& values.state == GameState::SongSelect {
|
||||
// Reseting pp's from result screen
|
||||
if values.prev_state == GameState::ResultScreen {
|
||||
values.current_pp = 0.0;
|
||||
}
|
||||
|
||||
values.update_current_pp(&mut state.ivalues);
|
||||
values.update_stars_and_ss_pp();
|
||||
values.adjust_bpm();
|
||||
}
|
||||
|
||||
// Update stars when entering `Playing` state
|
||||
if values.prev_state != GameState::Playing
|
||||
&& values.state == GameState::Playing {
|
||||
values.reset_gameplay();
|
||||
values.update_stars_and_ss_pp();
|
||||
values.adjust_bpm();
|
||||
}
|
||||
|
||||
// Handle mods changes inside `SongSelect` state
|
||||
if values.state == GameState::SongSelect
|
||||
&& values.prev_menu_mods != values.menu_mods {
|
||||
values.update_stars_and_ss_pp();
|
||||
values.update_current_pp(&mut state.ivalues);
|
||||
values.adjust_bpm();
|
||||
}
|
||||
|
||||
values.prev_menu_mode = values.menu_mode;
|
||||
values.prev_menu_mods = menu_mods;
|
||||
values.prev_state = values.state;
|
||||
|
||||
Ok(())
|
||||
}
|
1065
src-tauri/src/structs.rs
Normal file
1065
src-tauri/src/structs.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user