feat: integrated new FPS unlocker

This commit is contained in:
mkrsym1 2023-12-28 00:28:46 +02:00
parent e3f4677a2b
commit ebebb65e74
4 changed files with 21 additions and 127 deletions

View file

@ -1,15 +1,11 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue; use serde_json::Value as JsonValue;
use crate::config::schema_blanks::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Config { pub struct Config {
pub fps: u64, // TODO: Fps enum pub fps: u64, // TODO: Fps enum
pub power_saving: bool, pub periodic_writes: bool,
pub monitor: u64, pub interval: u64
pub window_mode: WindowMode,
pub priority: u64
} }
impl Default for Config { impl Default for Config {
@ -17,10 +13,8 @@ impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
fps: 120, fps: 120,
power_saving: false, periodic_writes: true,
monitor: 1, interval: 5000
window_mode: WindowMode::default(),
priority: 3
} }
} }
} }
@ -35,24 +29,14 @@ impl From<&JsonValue> for Config {
None => default.fps None => default.fps
}, },
power_saving: match value.get("power_saving") { periodic_writes: match value.get("periodic_writes") {
Some(value) => value.as_bool().unwrap_or(default.power_saving), Some(value) => value.as_bool().unwrap_or(default.periodic_writes),
None => default.power_saving None => default.periodic_writes
}, },
monitor: match value.get("monitor") { interval: match value.get("interval") {
Some(value) => value.as_u64().unwrap_or(default.monitor), Some(value) => value.as_u64().unwrap_or(default.interval),
None => default.monitor None => default.interval
},
window_mode: match value.get("window_mode") {
Some(value) => WindowMode::from(value),
None => default.window_mode
},
priority: match value.get("priority") {
Some(value) => value.as_u64().unwrap_or(default.priority),
None => default.priority
} }
} }
} }

View file

@ -4,13 +4,9 @@ use md5::{Md5, Digest};
use anime_game_core::installer::downloader::Downloader; use anime_game_core::installer::downloader::Downloader;
use super::config::schema::prelude::FpsUnlockerConfig;
pub mod config_schema;
const LATEST_INFO: (&str, &str) = ( const LATEST_INFO: (&str, &str) = (
"cff81830eebd3566d51b73ffaa444035", "53cb34d292d6b1dd1d8310318fe49bad",
"https://github.com/34736384/genshin-fps-unlock/releases/download/v2.2.0/unlockfps_clr.exe" "https://codeberg.org/mkrsym1/fpsunlock/releases/download/v1.0.2/fpsunlock.exe"
); );
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -22,13 +18,13 @@ impl FpsUnlocker {
/// Get FpsUnlocker from its containment directory /// Get FpsUnlocker from its containment directory
/// ///
/// Returns /// Returns
/// - `Err(..)` if failed to read `unlocker.exe` file /// - `Err(..)` if failed to read `fpsunlock.exe` file
/// - `Ok(None)` if version is not latest /// - `Ok(None)` if version is not latest
/// - `Ok(..)` if version is latest /// - `Ok(..)` if version is latest
pub fn from_dir<T: Into<PathBuf> + std::fmt::Debug>(dir: T) -> anyhow::Result<Option<Self>> { pub fn from_dir<T: Into<PathBuf> + std::fmt::Debug>(dir: T) -> anyhow::Result<Option<Self>> {
let dir = dir.into(); let dir = dir.into();
let hash = format!("{:x}", Md5::digest(std::fs::read(dir.join("unlocker.exe"))?)); let hash = format!("{:x}", Md5::digest(std::fs::read(dir.join("fpsunlock.exe"))?));
Ok(if hash == LATEST_INFO.0 { Ok(if hash == LATEST_INFO.0 {
Some(Self { dir }) Some(Self { dir })
@ -51,7 +47,7 @@ impl FpsUnlocker {
std::fs::create_dir_all(&dir)?; std::fs::create_dir_all(&dir)?;
} }
match downloader.download(dir.join("unlocker.exe"), |_, _| {}) { match downloader.download(dir.join("fpsunlock.exe"), |_, _| {}) {
Ok(_) => Ok(Self { dir }), Ok(_) => Ok(Self { dir }),
Err(err) => { Err(err) => {
tracing::error!("Downloading failed: {err}"); tracing::error!("Downloading failed: {err}");
@ -68,24 +64,11 @@ impl FpsUnlocker {
#[inline] #[inline]
pub fn get_binary_in<T: Into<PathBuf>>(dir: T) -> PathBuf { pub fn get_binary_in<T: Into<PathBuf>>(dir: T) -> PathBuf {
dir.into().join("unlocker.exe") dir.into().join("fpsunlock.exe")
} }
#[inline] #[inline]
pub fn dir(&self) -> &PathBuf { pub fn dir(&self) -> &PathBuf {
&self.dir &self.dir
} }
/// Generate and save FPS unlocker config file to the game's directory
#[tracing::instrument(level = "debug", ret)]
pub fn update_config(&self, config: FpsUnlockerConfig) -> anyhow::Result<()> {
tracing::debug!("Updating FPS unlocker config");
let config = config_schema::ConfigSchema::from_config(config);
Ok(std::fs::write(
self.dir.join("fps_config.json"),
config.json()?
)?)
}
} }

View file

@ -1,69 +0,0 @@
use serde::{Serialize, Deserialize};
use crate::config::schema_blanks::prelude::*;
use super::FpsUnlockerConfig;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct ConfigSchema {
pub DllList: Vec<String>,
pub Priority: u64,
pub MonitorNum: u64,
pub CustomResY: u64,
pub CustomResX: u64,
pub FPSTarget: u64,
pub UsePowerSave: bool,
pub StartMinimized: bool,
pub IsExclusiveFullscreen: bool,
pub UseCustomRes: bool,
pub Fullscreen: bool,
pub PopupWindow: bool,
pub AutoClose: bool,
pub AutoDisableVSync: bool,
pub AutoStart: bool,
pub GamePath: Option<String>
}
impl Default for ConfigSchema {
fn default() -> Self {
Self {
DllList: vec![],
Priority: 3,
MonitorNum: 1,
CustomResY: 1080,
CustomResX: 1920,
FPSTarget: 120,
UsePowerSave: false,
IsExclusiveFullscreen: false,
UseCustomRes: false,
Fullscreen: false,
PopupWindow: false,
AutoDisableVSync: true,
GamePath: None,
// Launcher-specific settings
AutoStart: true,
AutoClose: true,
StartMinimized: true
}
}
}
impl ConfigSchema {
pub fn from_config(config: FpsUnlockerConfig) -> Self {
Self {
FPSTarget: config.fps,
UsePowerSave: config.power_saving,
PopupWindow: config.window_mode == WindowMode::Popup,
Fullscreen: config.window_mode == WindowMode::Fullscreen,
MonitorNum: config.monitor,
Priority: config.priority,
..Self::default()
}
}
pub fn json(&self) -> serde_json::Result<String> {
serde_json::to_string(self)
}
}

View file

@ -89,8 +89,7 @@ pub fn run() -> anyhow::Result<()> {
// Prepare fps unlocker // Prepare fps unlocker
// 1) Download if needed // 1) Download if needed
// 2) Generate config file // 2) Generate fps_unlocker.bat
// 3) Generate fpsunlocker.bat from launcher.bat
#[cfg(feature = "fps-unlocker")] #[cfg(feature = "fps-unlocker")]
if config.game.enhancements.fps_unlocker.enabled { if config.game.enhancements.fps_unlocker.enabled {
@ -115,14 +114,11 @@ pub fn run() -> anyhow::Result<()> {
} }
}; };
// Generate FPS unlocker config file
if let Err(err) = unlocker.update_config(config.game.enhancements.fps_unlocker.config) {
return Err(anyhow::anyhow!("Failed to update FPS unlocker config: {err}"));
}
// If patch applying is disabled, then game_executable is either GenshinImpact.exe or YuanShen.exe // If patch applying is disabled, then game_executable is either GenshinImpact.exe or YuanShen.exe
// so we don't need to check it here // so we don't need to check it here
std::fs::write(game_path.join("fps_unlocker.bat"), format!("start {game_executable} %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir().to_string_lossy()))?; let unlocker_config = &config.game.enhancements.fps_unlocker.config;
let unlocker_interval = if unlocker_config.periodic_writes { unlocker_config.interval as i64 } else { -1 };
std::fs::write(game_path.join("fps_unlocker.bat"), format!("start {game_executable} %*\n\nZ:\ncd \"{}\"\nstart fpsunlock.exe {} {unlocker_interval}", unlocker.dir().to_string_lossy(), unlocker_config.fps))?;
} }
// Generate `config.ini` if environment emulation feature is presented // Generate `config.ini` if environment emulation feature is presented
@ -314,7 +310,7 @@ pub fn run() -> anyhow::Result<()> {
let output = Command::new("ps").arg("-A").stdout(Stdio::piped()).output()?; let output = Command::new("ps").arg("-A").stdout(Stdio::piped()).output()?;
let output = String::from_utf8_lossy(&output.stdout); let output = String::from_utf8_lossy(&output.stdout);
if !output.contains("GenshinImpact.e") && !output.contains("YuanShen.exe") && !output.contains("unlocker.exe") { if !output.contains("GenshinImpact.e") && !output.contains("YuanShen.exe") && !output.contains("fpsunlock.exe") {
break; break;
} }