diff --git a/res/saekawa.toml b/res/saekawa.toml index 300f32b..a294653 100644 --- a/res/saekawa.toml +++ b/res/saekawa.toml @@ -3,6 +3,8 @@ enable = true # Whether the hook should export your class medals and emblems or not. export_class = true +# Whether FAILED should override FULL COMBO and ALL JUSTICE. +fail_over_lamp = false # Timeout for web requests, in milliseconds timeout = 3000 diff --git a/src/configuration.rs b/src/configuration.rs index 702e7f9..e2eec4d 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -28,8 +28,13 @@ impl Configuration { pub struct GeneralConfiguration { #[serde(default = "default_true")] pub enable: bool, - #[serde(default)] + + #[serde(default = "default_true")] pub export_class: bool, + + #[serde(default = "default_false")] + pub fail_over_lamp: bool, + #[serde(default = "default_timeout")] pub timeout: u64, } @@ -38,6 +43,10 @@ fn default_true() -> bool { true } +fn default_false() -> bool { + false +} + fn default_timeout() -> u64 { 3000 } diff --git a/src/saekawa.rs b/src/saekawa.rs index 9c6752c..68bd177 100644 --- a/src/saekawa.rs +++ b/src/saekawa.rs @@ -14,7 +14,7 @@ use winapi::{ }; use crate::{ - helpers::{call_tachi, read_hinternet_url, read_potentially_deflated_buffer, self}, + helpers::{call_tachi, read_hinternet_url, read_potentially_deflated_buffer, request_tachi}, types::{ game::UpsertUserAllRequest, tachi::{ClassEmblem, Import, ImportClasses, ImportScore}, @@ -33,7 +33,7 @@ pub fn hook_init() -> Result<()> { return Ok(()); } - let resp: serde_json::Value = helpers::request_tachi("GET", TACHI_STATUS_URL.as_str(), None::<()>)?; + let resp: serde_json::Value = request_tachi("GET", TACHI_STATUS_URL.as_str(), None::<()>)?; let user_id = resp["body"]["whoami"] .as_u64() .ok_or(anyhow::anyhow!("Couldn't parse user from Tachi response"))?; @@ -47,14 +47,13 @@ pub fn hook_init() -> Result<()> { }; unsafe { - DetourWriteData.initialize(winhttpwritedata, move |a, b, c, d| { - winhttpwritedata_hook(a, b, c, d) - })?; - - DetourWriteData.enable()?; + DetourWriteData + .initialize(winhttpwritedata, winhttpwritedata_hook)? + .enable()?; }; info!("Hook successfully initialized"); + Ok(()) } @@ -68,7 +67,7 @@ pub fn hook_release() -> Result<()> { Ok(()) } -unsafe fn winhttpwritedata_hook( +fn winhttpwritedata_hook( h_request: HINTERNET, lp_buffer: LPCVOID, dw_number_of_bytes_to_write: DWORD, @@ -76,12 +75,14 @@ unsafe fn winhttpwritedata_hook( ) -> BOOL { debug!("hit winhttpwritedata"); - let orig = || DetourWriteData.call( - h_request, - lp_buffer, - dw_number_of_bytes_to_write, - lpdw_number_of_bytes_written, - ); + let orig = || unsafe { + DetourWriteData.call( + h_request, + lp_buffer, + dw_number_of_bytes_to_write, + lpdw_number_of_bytes_written, + ) + }; let url = match read_hinternet_url(h_request) { Ok(url) => url, @@ -92,10 +93,12 @@ unsafe fn winhttpwritedata_hook( }; debug!("winhttpwritedata URL: {url}"); - let request_body = match read_potentially_deflated_buffer( - lp_buffer as *const u8, - dw_number_of_bytes_to_write as usize, - ) { + let request_body = match unsafe { + read_potentially_deflated_buffer( + lp_buffer as *const u8, + dw_number_of_bytes_to_write as usize, + ) + } { Ok(data) => data, Err(err) => { error!("There was an error reading the request body: {:#}", err); @@ -141,7 +144,9 @@ unsafe fn winhttpwritedata_hook( .user_playlog_list .into_iter() .filter_map(|playlog| { - if let Ok(score) = ImportScore::try_from(playlog) { + if let Ok(score) = + ImportScore::try_from_playlog(playlog, CONFIGURATION.general.fail_over_lamp) + { if score.difficulty.as_str() == "WORLD'S END" { return None; } @@ -157,7 +162,10 @@ unsafe fn winhttpwritedata_hook( return orig(); } - if classes.clone().is_some_and(|v| v.dan.is_none() && v.emblem.is_none()) { + if classes + .clone() + .is_some_and(|v| v.dan.is_none() && v.emblem.is_none()) + { return orig(); } } diff --git a/src/types/tachi.rs b/src/types/tachi.rs index aa55c71..d05792c 100644 --- a/src/types/tachi.rs +++ b/src/types/tachi.rs @@ -1,4 +1,4 @@ -use anyhow::anyhow; +use anyhow::{anyhow, Result}; use chrono::{FixedOffset, NaiveDateTime, TimeZone}; use num_enum::TryFromPrimitive; use serde::{Deserialize, Serialize}; @@ -107,11 +107,11 @@ pub struct OptionalMetrics { pub max_combo: u32, } -impl TryFrom for ImportScore { - type Error = anyhow::Error; - - fn try_from(p: UserPlaylog) -> Result { - let lamp = if p.is_all_justice { +impl ImportScore { + pub fn try_from_playlog(p: UserPlaylog, fail_over_lamp: bool) -> Result { + let lamp = if !p.is_clear && fail_over_lamp { + TachiLamp::Failed + } else if p.is_all_justice { if p.judge_justice + p.judge_attack + p.judge_guilty == 0 { TachiLamp::AllJusticeCritical } else {