Merge pull request 'dev-video' (#3) from dev-video into master
Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -38,4 +38,8 @@ yarn-error.log*
|
||||
*.tsbuildinfo
|
||||
|
||||
.env
|
||||
.env.*
|
||||
.env.*
|
||||
|
||||
temp/
|
||||
src-tauri/temp/
|
||||
log/
|
||||
2
src-tauri/.gitignore
vendored
2
src-tauri/.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
/target/
|
||||
@@ -2039,6 +2039,21 @@
|
||||
"type": "string",
|
||||
"const": "autostart:deny-is-enabled"
|
||||
},
|
||||
{
|
||||
"description": "Allows reading the CLI matches",
|
||||
"type": "string",
|
||||
"const": "cli:default"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:allow-cli-matches"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:deny-cli-matches"
|
||||
},
|
||||
{
|
||||
"description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n",
|
||||
"type": "string",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::steam;
|
||||
use crate::tool::*;
|
||||
use crate::vdf::preset;
|
||||
use crate::vdf::preset::VideoConfig;
|
||||
use crate::wrap_err;
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -55,7 +56,7 @@ pub fn get_powerplan() -> Result<i32, String> {
|
||||
let powerplan = powerplan::get_powerplan()?;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let powerplan = powerplan::PowerPlanMode::Other.into();
|
||||
let powerplan = powerplan::PowerPlanMode::Other as i32;
|
||||
|
||||
Ok(powerplan)
|
||||
}
|
||||
@@ -81,6 +82,31 @@ pub fn set_auto_login_user(user: &str) -> Result<String, String> {
|
||||
Ok(format!("Set auto login user to {}", user))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_cs2_video_config(steam_dir: &str, steam_id32: u32) -> Result<VideoConfig, String> {
|
||||
let p = format!(
|
||||
"{}/userdata/{}/730/local/cfg/cs2_video.txt",
|
||||
steam_dir, steam_id32
|
||||
);
|
||||
let video = preset::get_cs2_video(p.as_str()).map_err(|e| e.to_string())?;
|
||||
Ok(video)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn set_cs2_video_config(
|
||||
steam_dir: &str,
|
||||
steam_id32: u32,
|
||||
video_config: VideoConfig,
|
||||
) -> Result<(), String> {
|
||||
let p = format!(
|
||||
"{}/userdata/{}/730/local/cfg/cs2_video.txt",
|
||||
steam_dir, steam_id32
|
||||
);
|
||||
preset::set_cs2_video(p.as_str(), video_config).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn check_path(path: &str) -> Result<bool, String> {
|
||||
Ok(std::path::Path::new(&path).exists())
|
||||
|
||||
@@ -129,6 +129,8 @@ fn main() {
|
||||
cmds::set_powerplan,
|
||||
cmds::get_steam_users,
|
||||
cmds::set_auto_login_user,
|
||||
cmds::get_cs2_video_config,
|
||||
cmds::set_cs2_video_config,
|
||||
cmds::check_path,
|
||||
on_button_clicked
|
||||
])
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
// const DETACHED_PROCESS: u32 = 0x00000008;
|
||||
|
||||
pub fn kill(name: &str) -> String {
|
||||
#[cfg(windows)]
|
||||
Command::new("taskkill")
|
||||
.args(&["/IM", name, "/F"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
@@ -15,10 +17,16 @@ pub fn kill(name: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn run_steam() -> std::io::Result<std::process::Output> {
|
||||
Command::new("cmd")
|
||||
#[cfg(target_os = "windows")]
|
||||
return Command::new("cmd")
|
||||
.args(&["/C", "start", "steam://run"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.output();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
Command::new("open")
|
||||
.args(&["-a", "Steam"])
|
||||
.output()
|
||||
}
|
||||
|
||||
pub fn get_exe_path(name: &str) -> Result<String, std::io::Error> {
|
||||
@@ -30,11 +38,17 @@ pub fn get_exe_path(name: &str) -> Result<String, std::io::Error> {
|
||||
// 进程路径
|
||||
let command = format!("Get-Process {} | Select-Object path", name);
|
||||
let args = command.split_whitespace().collect::<Vec<&str>>();
|
||||
#[cfg(windows)]
|
||||
let output = Command::new("powershell.exe")
|
||||
.args(&args)
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let output = Command::new("osascript")
|
||||
.args(&["-e", &format!("tell application \"{}\" to get path to me", name)])
|
||||
.output()?;
|
||||
|
||||
let out = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
|
||||
if out.contains("Path") {
|
||||
@@ -54,6 +68,7 @@ pub fn get_exe_path(name: &str) -> Result<String, std::io::Error> {
|
||||
pub fn open_path(path: &str) -> Result<(), std::io::Error> {
|
||||
// path中所有/ 转换为 \
|
||||
let path = path.replace("/", "\\");
|
||||
#[cfg(windows)]
|
||||
Command::new("cmd.exe")
|
||||
.args(["/c", "start", "", &path])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
|
||||
@@ -39,6 +39,7 @@ impl PowerPlan {
|
||||
.get(&mode)
|
||||
.ok_or("Invalid power plan number (expect from 1 to 4)")?;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let output = Command::new("powercfg")
|
||||
.arg("/S")
|
||||
.arg(guid)
|
||||
@@ -46,6 +47,14 @@ impl PowerPlan {
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute powercfg command: {}", e))?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let output = Command::new("pmset")
|
||||
.arg("-g")
|
||||
.arg("plan")
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute pmset command: {}", e))?;
|
||||
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"Powercfg command failed: {}",
|
||||
@@ -57,12 +66,20 @@ impl PowerPlan {
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Result<i32, String> {
|
||||
#[cfg(windows)]
|
||||
let output = Command::new("powercfg")
|
||||
.arg("/L")
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute powercfg command: {}", e))?;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let output = Command::new("pmset")
|
||||
.arg("-g")
|
||||
.arg("plan")
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute pmset command: {}", e))?;
|
||||
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
let re = regex::Regex::new(r"GUID:\s+(\S+)\s+\(\S+\)\s+\*")
|
||||
.map_err(|e| format!("Failed to compile regex: {}", e))?;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub mod parse;
|
||||
pub mod preset;
|
||||
pub mod preset;
|
||||
@@ -1,11 +1,12 @@
|
||||
pub fn to_json(vdf_data: &str) -> String {
|
||||
let linebreak = match std::env::consts::OS {
|
||||
"macos" => "\r",
|
||||
"macos" => "\n", //"\r",
|
||||
"windows" => "\n",
|
||||
"linux" => "\n",
|
||||
_ => "\n",
|
||||
};
|
||||
|
||||
// NOTE: 这样会跳过顶层{}
|
||||
let startpoint = vdf_data.find('{').unwrap_or(0);
|
||||
let vdf_data = &vdf_data[startpoint..];
|
||||
|
||||
@@ -31,49 +32,108 @@ pub fn to_json(vdf_data: &str) -> String {
|
||||
json_data.push_str(&line);
|
||||
}
|
||||
|
||||
// let json_str = json_data
|
||||
json_data = json_data
|
||||
.replace(",}", "}")
|
||||
.trim_start_matches(": ")
|
||||
.trim_end_matches(',')
|
||||
.to_string();
|
||||
// json_data = format!("{{{}}}", json_str);
|
||||
|
||||
json_data
|
||||
return json_data;
|
||||
}
|
||||
|
||||
pub fn to_vdf(json_data: &str) -> String {
|
||||
let json_value: serde_json::Value = serde_json::from_str(json_data).unwrap();
|
||||
let mut vdf_data = String::new();
|
||||
build_vdf(&json_value, &mut vdf_data, 0);
|
||||
vdf_data
|
||||
}
|
||||
|
||||
fn build_vdf(json_value: &serde_json::Value, vdf_data: &mut String, indent_level: usize) {
|
||||
match json_value {
|
||||
serde_json::Value::Object(obj) => {
|
||||
for (key, value) in obj {
|
||||
vdf_data.push_str(&"\t".repeat(indent_level));
|
||||
vdf_data.push_str(&format!("\"{}\"\n", key));
|
||||
vdf_data.push_str(&"\t".repeat(indent_level));
|
||||
vdf_data.push_str("{\n");
|
||||
build_vdf(value, vdf_data, indent_level + 1);
|
||||
vdf_data.push_str(&"\t".repeat(indent_level));
|
||||
vdf_data.push_str("}\n");
|
||||
}
|
||||
}
|
||||
serde_json::Value::String(s) => {
|
||||
vdf_data.push_str(&"\t".repeat(indent_level));
|
||||
vdf_data.push_str(&format!("\"{}\"\t\t\"{}\"\n", s, s));
|
||||
}
|
||||
_ => {
|
||||
vdf_data.push_str(&"\t".repeat(indent_level));
|
||||
vdf_data.push_str(&format!("\"{}\"\t\t\"{}\"\n", json_value, json_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
static VDF_DATA: &str = r#""users"
|
||||
{
|
||||
"76561198315078806"
|
||||
{
|
||||
"AccountName" "_jerry_dota2"
|
||||
"PersonaName" "Rop紫(已黑化)"
|
||||
"RememberPassword" "1"
|
||||
"WantsOfflineMode" "0"
|
||||
"SkipOfflineModeWarning" "0"
|
||||
"AllowAutoLogin" "1"
|
||||
"MostRecent" "1"
|
||||
"Timestamp" "1742706884"
|
||||
}
|
||||
"76561198107125441"
|
||||
{
|
||||
"AccountName" "_im_ai_"
|
||||
"PersonaName" "Buongiorno"
|
||||
"RememberPassword" "1"
|
||||
"WantsOfflineMode" "0"
|
||||
"SkipOfflineModeWarning" "0"
|
||||
"AllowAutoLogin" "1"
|
||||
"MostRecent" "0"
|
||||
"Timestamp" "1739093763"
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
static JSON_DATA: &str = r#"{
|
||||
"users": {
|
||||
"76561198315078806": {
|
||||
"AccountName": "_jerry_dota2",
|
||||
"PersonaName": "Rop紫(已黑化)",
|
||||
"RememberPassword": "1",
|
||||
"WantsOfflineMode": "0",
|
||||
"SkipOfflineModeWarning": "0",
|
||||
"AllowAutoLogin": "1",
|
||||
"MostRecent": "1",
|
||||
"Timestamp": "1742706884"
|
||||
},
|
||||
"76561198107125441": {
|
||||
"AccountName": "_im_ai_",
|
||||
"PersonaName": "Buongiorno",
|
||||
"RememberPassword": "1",
|
||||
"WantsOfflineMode": "0",
|
||||
"SkipOfflineModeWarning": "0",
|
||||
"AllowAutoLogin": "1",
|
||||
"MostRecent": "0",
|
||||
"Timestamp": "1739093763"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
#[test]
|
||||
fn test_to_json() {
|
||||
let vdf_data = "\"users\"
|
||||
{
|
||||
\"76561198315078806\"
|
||||
{
|
||||
\"AccountName\" \"_jerry_dota2\"
|
||||
\"PersonaName\" \"Rop紫(已黑化)\"
|
||||
\"RememberPassword\" \"1\"
|
||||
\"WantsOfflineMode\" \"0\"
|
||||
\"SkipOfflineModeWarning\" \"0\"
|
||||
\"AllowAutoLogin\" \"1\"
|
||||
\"MostRecent\" \"1\"
|
||||
\"Timestamp\" \"1742706884\"
|
||||
}
|
||||
\"76561198107125441\"
|
||||
{
|
||||
\"AccountName\" \"_im_ai_\"
|
||||
\"PersonaName\" \"Buongiorno\"
|
||||
\"RememberPassword\" \"1\"
|
||||
\"WantsOfflineMode\" \"0\"
|
||||
\"SkipOfflineModeWarning\" \"0\"
|
||||
\"AllowAutoLogin\" \"1\"
|
||||
\"MostRecent\" \"0\"
|
||||
\"Timestamp\" \"1739093763\"
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
// let expected_json = r#"{"key1": "value1","key2": "value2","subkey": {"key3": "value3"}}"#;
|
||||
let json_data = to_json(vdf_data);
|
||||
let json_data = to_json(VDF_DATA);
|
||||
println!("{}", json_data);
|
||||
|
||||
// 解析json
|
||||
let json_value: serde_json::Value = serde_json::from_str(&json_data).unwrap();
|
||||
@@ -81,4 +141,12 @@ mod tests {
|
||||
println!("{}", json_value)
|
||||
// assert_eq!(to_json(vdf_data), expected_json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_vdf() {
|
||||
// let json_data = r#"{"key1": "value1","key2": "value2","subkey": {"key3": "value3"}}"#;
|
||||
let vdf_data = to_vdf(JSON_DATA);
|
||||
|
||||
println!("{}", vdf_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use base64::Engine;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
@@ -44,6 +45,81 @@ pub struct LocalUser {
|
||||
avatar_key: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct VideoConfig {
|
||||
version: String,
|
||||
vendor_id: String,
|
||||
device_id: String,
|
||||
cpu_level: String,
|
||||
gpu_mem_level: String,
|
||||
gpu_level: String,
|
||||
knowndevice: String,
|
||||
defaultres: String,
|
||||
defaultresheight: String,
|
||||
refreshrate_numerator: String,
|
||||
refreshrate_denominator: String,
|
||||
fullscreen: String,
|
||||
coop_fullscreen: String,
|
||||
nowindowborder: String,
|
||||
mat_vsync: String,
|
||||
fullscreen_min_on_focus_loss: String,
|
||||
high_dpi: String,
|
||||
auto_config: String,
|
||||
shaderquality: String,
|
||||
r_texturefilteringquality: String,
|
||||
msaa_samples: String,
|
||||
r_csgo_cmaa_enable: String,
|
||||
videocfg_shadow_quality: String,
|
||||
videocfg_dynamic_shadows: String,
|
||||
videocfg_texture_detail: String,
|
||||
videocfg_particle_detail: String,
|
||||
videocfg_ao_detail: String,
|
||||
videocfg_hdr_detail: String,
|
||||
videocfg_fsr_detail: String,
|
||||
monitor_index: String,
|
||||
r_low_latency: String,
|
||||
aspectratiomode: String,
|
||||
}
|
||||
|
||||
impl Default for VideoConfig {
|
||||
fn default() -> Self {
|
||||
VideoConfig {
|
||||
version: "15".to_string(),
|
||||
vendor_id: "0".to_string(),
|
||||
device_id: "0".to_string(),
|
||||
cpu_level: "3".to_string(),
|
||||
gpu_mem_level: "3".to_string(),
|
||||
gpu_level: "3".to_string(),
|
||||
knowndevice: "0".to_string(),
|
||||
defaultres: "1920".to_string(),
|
||||
defaultresheight: "1080".to_string(),
|
||||
refreshrate_numerator: "144".to_string(),
|
||||
refreshrate_denominator: "1".to_string(),
|
||||
fullscreen: "1".to_string(),
|
||||
coop_fullscreen: "0".to_string(),
|
||||
nowindowborder: "1".to_string(),
|
||||
mat_vsync: "0".to_string(),
|
||||
fullscreen_min_on_focus_loss: "1".to_string(),
|
||||
high_dpi: "0".to_string(),
|
||||
auto_config: "2".to_string(),
|
||||
shaderquality: "0".to_string(),
|
||||
r_texturefilteringquality: "3".to_string(),
|
||||
msaa_samples: "2".to_string(),
|
||||
r_csgo_cmaa_enable: "0".to_string(),
|
||||
videocfg_shadow_quality: "0".to_string(),
|
||||
videocfg_dynamic_shadows: "1".to_string(),
|
||||
videocfg_texture_detail: "1".to_string(),
|
||||
videocfg_particle_detail: "0".to_string(),
|
||||
videocfg_ao_detail: "0".to_string(),
|
||||
videocfg_hdr_detail: "3".to_string(),
|
||||
videocfg_fsr_detail: "0".to_string(),
|
||||
monitor_index: "0".to_string(),
|
||||
r_low_latency: "1".to_string(),
|
||||
aspectratiomode: "0".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_login_users(steam_dir: &str) -> Result<Vec<LoginUser>> {
|
||||
let t_path = Path::new(steam_dir).join("config/loginusers.vdf");
|
||||
if !t_path.exists() {
|
||||
@@ -151,6 +227,9 @@ pub fn parse_local_users(steam_dir: &str) -> Result<Vec<LocalUser>> {
|
||||
let json_data = super::parse::to_json(&data);
|
||||
let kv: HashMap<String, Value> = serde_json::from_str(&json_data)?;
|
||||
|
||||
// 剥离顶层 UserLocalConfigStore
|
||||
// let kv = kv.get("UserLocalConfigStore").and_then(|v| v.as_object()).unwrap();
|
||||
|
||||
// 获取 friends 节点
|
||||
let friends = kv.get("friends").and_then(|v| v.as_object());
|
||||
if friends.is_none() {
|
||||
@@ -270,6 +349,184 @@ fn read_avatar(steam_dir: &str, steam_id64: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cs2_video(file_path: &str) -> Result<VideoConfig> {
|
||||
// TODO: no kv
|
||||
let data = fs::read_to_string(file_path)?;
|
||||
let json_data = super::parse::to_json(&data);
|
||||
let kv: HashMap<String, String> = serde_json::from_str(&json_data)?;
|
||||
let video_config = VideoConfig {
|
||||
version: kv.get("Version").unwrap_or(&"".to_string()).to_string(),
|
||||
vendor_id: kv.get("VendorID").unwrap_or(&"".to_string()).to_string(),
|
||||
device_id: kv.get("DeviceID").unwrap_or(&"".to_string()).to_string(),
|
||||
cpu_level: kv
|
||||
.get("setting.cpu_level")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
gpu_mem_level: kv
|
||||
.get("setting.gpu_mem_level")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
gpu_level: kv
|
||||
.get("setting.gpu_level")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
knowndevice: kv
|
||||
.get("setting.knowndevice")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
defaultres: kv
|
||||
.get("setting.defaultres")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
defaultresheight: kv
|
||||
.get("setting.defaultresheight")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
refreshrate_numerator: kv
|
||||
.get("setting.refreshrate_numerator")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
refreshrate_denominator: kv
|
||||
.get("setting.refreshrate_denominator")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
fullscreen: kv
|
||||
.get("setting.fullscreen")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
coop_fullscreen: kv
|
||||
.get("setting.coop_fullscreen")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
nowindowborder: kv
|
||||
.get("setting.nowindowborder")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
mat_vsync: kv
|
||||
.get("setting.mat_vsync")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
fullscreen_min_on_focus_loss: kv
|
||||
.get("setting.fullscreen_min_on_focus_loss")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
high_dpi: kv
|
||||
.get("setting.high_dpi")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
auto_config: kv.get("AutoConfig").unwrap_or(&"".to_string()).to_string(),
|
||||
shaderquality: kv
|
||||
.get("setting.shaderquality")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
r_texturefilteringquality: kv
|
||||
.get("setting.r_texturefilteringquality")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
msaa_samples: kv
|
||||
.get("setting.msaa_samples")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
r_csgo_cmaa_enable: kv
|
||||
.get("setting.r_csgo_cmaa_enable")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
videocfg_shadow_quality: kv
|
||||
.get("setting.videocfg_shadow_quality")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
videocfg_dynamic_shadows: kv
|
||||
.get("setting.videocfg_dynamic_shadows")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
videocfg_texture_detail: kv
|
||||
.get("setting.videocfg_texture_detail")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
videocfg_particle_detail: kv
|
||||
.get("setting.videocfg_particle_detail")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
videocfg_ao_detail: kv
|
||||
.get("setting.videocfg_ao_detail")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
videocfg_hdr_detail: kv
|
||||
.get("setting.videocfg_hdr_detail")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
videocfg_fsr_detail: kv
|
||||
.get("setting.videocfg_fsr_detail")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
monitor_index: kv
|
||||
.get("setting.monitor_index")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
r_low_latency: kv
|
||||
.get("setting.r_low_latency")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
aspectratiomode: kv
|
||||
.get("setting.aspectratiomode")
|
||||
.unwrap_or(&"".to_string())
|
||||
.to_string(),
|
||||
};
|
||||
Ok(video_config)
|
||||
}
|
||||
|
||||
pub fn set_cs2_video(file_path: &str, data: VideoConfig) -> Result<()> {
|
||||
// 读取文件内容
|
||||
let file_content = fs::read_to_string(file_path)?;
|
||||
|
||||
// 定义正则表达式匹配模式
|
||||
let re = Regex::new(r#""(setting\.\w+)"\s+"-?\d+""#).unwrap();
|
||||
|
||||
// 替换字段值
|
||||
let updated_content = re.replace_all(&file_content, |caps: ®ex::Captures| {
|
||||
let key = &caps[1]; // 捕获的键名
|
||||
let value = match key {
|
||||
"Version" => &data.version,
|
||||
"VendorID" => &data.vendor_id,
|
||||
"DeviceID" => &data.device_id,
|
||||
"setting.cpu_level" => &data.cpu_level,
|
||||
"setting.gpu_mem_level" => &data.gpu_mem_level,
|
||||
"setting.gpu_level" => &data.gpu_level,
|
||||
"setting.knowndevice" => &data.knowndevice,
|
||||
"setting.defaultres" => &data.defaultres,
|
||||
"setting.defaultresheight" => &data.defaultresheight,
|
||||
"setting.refreshrate_numerator" => &data.refreshrate_numerator,
|
||||
"setting.refreshrate_denominator" => &data.refreshrate_denominator,
|
||||
"setting.fullscreen" => &data.fullscreen,
|
||||
"setting.coop_fullscreen" => &data.coop_fullscreen,
|
||||
"setting.nowindowborder" => &data.nowindowborder,
|
||||
"setting.mat_vsync" => &data.mat_vsync,
|
||||
"setting.fullscreen_min_on_focus_loss" => &data.fullscreen_min_on_focus_loss,
|
||||
"setting.high_dpi" => &data.high_dpi,
|
||||
"AutoConfig" => &data.auto_config,
|
||||
"setting.shaderquality" => &data.shaderquality,
|
||||
"setting.r_texturefilteringquality" => &data.r_texturefilteringquality,
|
||||
"setting.msaa_samples" => &data.msaa_samples,
|
||||
"setting.r_csgo_cmaa_enable" => &data.r_csgo_cmaa_enable,
|
||||
"setting.videocfg_shadow_quality" => &data.videocfg_shadow_quality,
|
||||
"setting.videocfg_dynamic_shadows" => &data.videocfg_dynamic_shadows,
|
||||
"setting.videocfg_texture_detail" => &data.videocfg_texture_detail,
|
||||
"setting.videocfg_particle_detail" => &data.videocfg_particle_detail,
|
||||
"setting.videocfg_ao_detail" => &data.videocfg_ao_detail,
|
||||
"setting.videocfg_hdr_detail" => &data.videocfg_hdr_detail,
|
||||
"setting.videocfg_fsr_detail" => &data.videocfg_fsr_detail,
|
||||
"setting.monitor_index" => &data.monitor_index,
|
||||
"setting.r_low_latency" => &data.r_low_latency,
|
||||
"setting.aspectratiomode" => &data.aspectratiomode,
|
||||
_ => "", // 默认情况
|
||||
};
|
||||
format!(r#""{}" "{}""#, key, value)
|
||||
});
|
||||
|
||||
fs::write(file_path, updated_content.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -279,4 +536,25 @@ mod tests {
|
||||
let users = get_users("D:\\Programs\\Steam").unwrap();
|
||||
println!("{:?}", users);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cs2_video() {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let file_path = format!("{}/src/vdf/tests/cs2_video.txt", manifest_dir);
|
||||
let video_config = get_cs2_video(&file_path).unwrap();
|
||||
println!("{:?}", video_config);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_cs2_video() {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let file_path = format!("{}/temp/cs2_video.txt", manifest_dir);
|
||||
fs::copy(
|
||||
format!("{}/src/vdf/tests/cs2_video.txt", manifest_dir),
|
||||
file_path.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let video_config = VideoConfig::default();
|
||||
set_cs2_video(&file_path, video_config).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
},
|
||||
"productName": "CS工具箱",
|
||||
"mainBinaryName": "cstb",
|
||||
"version": "0.0.5-beta.4",
|
||||
"version": "0.0.5",
|
||||
"identifier": "upup.cool",
|
||||
"plugins": {
|
||||
"deep-link": {
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
|
||||
addToast({ title: `电源计划已切换 → ${PowerPlans[current].title}` })
|
||||
})
|
||||
})
|
||||
}, [])
|
||||
|
||||
// 检测steam路径和游戏路径是否有效
|
||||
const debounceSteamDir = useDebounce(steam.state.steamDir, {
|
||||
|
||||
@@ -1,178 +1,452 @@
|
||||
import { CloseSmall, Down, Edit, Plus, SettingConfig, Up } from "@icon-park/react"
|
||||
import { useState } from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
||||
import { ToolButton } from "../window/ToolButton"
|
||||
import { addToast, NumberInput, Tab, Tabs, Tooltip } from "@heroui/react"
|
||||
import { motion } from "framer-motion"
|
||||
import { useToolStore } from "@/store/tool"
|
||||
import { useToolStore, VideoSetting as VideoConfig, VideoSettingTemplate } from "@/store/tool"
|
||||
import { useSteamStore } from "@/store/steam"
|
||||
|
||||
const VideoSetting = () => {
|
||||
const [hide, setHide] = useState(false)
|
||||
const [edit, setEdit] = useState(false)
|
||||
const tool = useToolStore()
|
||||
// const [launchOpt, setLaunchOpt] = useState(tool.state.VideoSettings[tool.state.launchIndex] || "")
|
||||
const steam = useSteamStore()
|
||||
const videoSettings = (video: VideoConfig) => {
|
||||
return [
|
||||
{
|
||||
type: "fullscreen",
|
||||
title: "全屏",
|
||||
value: video.fullscreen === "1" ? "全屏" : "窗口",
|
||||
options: ["窗口", "全屏"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
窗口: "0",
|
||||
全屏: "1",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "mat_vsync",
|
||||
title: "垂直同步",
|
||||
value: video.mat_vsync === "1" ? "开启" : "关闭",
|
||||
options: ["关闭", "开启"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
关闭: "0",
|
||||
开启: "1",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "r_low_latency",
|
||||
title: "低延迟模式",
|
||||
value: video.r_low_latency === "1" ? "开启" : "关闭",
|
||||
options: ["关闭", "开启"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
关闭: "0",
|
||||
开启: "1",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
// TODO: 改选项不在 cs2_video.txt 中
|
||||
// {
|
||||
// type: "r_player_visible_mode",
|
||||
// title: "增强角色对比度",
|
||||
// value: video.r_csgo_cmaa_enable === "1" ? "启用" : "禁用",
|
||||
// options: ["禁用", "启用"],
|
||||
// },
|
||||
{
|
||||
type: "r_csgo_cmaa_enable",
|
||||
title: "CMAA2抗锯齿",
|
||||
value: video.r_csgo_cmaa_enable === "1" ? "开启" : "关闭",
|
||||
options: ["关闭", "开启"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
关闭: "0",
|
||||
开启: "1",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "msaa_samples",
|
||||
title: "多重采样抗锯齿",
|
||||
value:
|
||||
{
|
||||
0: "无",
|
||||
2: "2X MSAA",
|
||||
4: "4X MSAA",
|
||||
8: "8X MSAA",
|
||||
}[parseInt(video.msaa_samples, 10)] || "无",
|
||||
options: ["无", "2X MSAA", "4X MSAA", "8X MSAA"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
无: "0",
|
||||
"2X MSAA": "2",
|
||||
"4X MSAA": "4",
|
||||
"8X MSAA": "8",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "videocfg_shadow_quality",
|
||||
title: "全局阴影效果",
|
||||
value: ["低", "中", "高", "非常高"][parseInt(video.videocfg_shadow_quality, 10)] || "低",
|
||||
options: ["低", "中", "高", "非常高"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
低: "0",
|
||||
中: "1",
|
||||
高: "2",
|
||||
非常高: "3",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "videocfg_dynamic_shadows",
|
||||
title: "动态阴影",
|
||||
value: ["仅限日光", "全部"][parseInt(video.videocfg_dynamic_shadows, 10)] || "仅限日光",
|
||||
options: ["仅限日光", "全部"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
仅限日光: "0",
|
||||
全部: "1",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "videocfg_texture_detail",
|
||||
title: "模型/贴图细节",
|
||||
value: ["低", "中", "高"][parseInt(video.videocfg_texture_detail, 10)] || "低",
|
||||
options: ["低", "中", "高"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
低: "0",
|
||||
中: "1",
|
||||
高: "2",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "r_texturefilteringquality",
|
||||
title: "贴图过滤模式",
|
||||
value:
|
||||
["双线性", "三线性", "异向 2X", "异向 4X", "异向 8X", "异向 16X"][
|
||||
parseInt(video.r_texturefilteringquality, 10)
|
||||
] || "双线性",
|
||||
options: ["双线性", "三线性", "异向 2X", "异向 4X", "异向 8X", "异向 16X"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
双线性: "0",
|
||||
三线性: "1",
|
||||
"异向 2X": "2",
|
||||
"异向 4X": "3",
|
||||
"异向 8X": "4",
|
||||
"异向 16X": "5",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "shaderquality",
|
||||
title: "光影细节",
|
||||
value: video.shaderquality === "1" ? "高" : "低",
|
||||
options: ["低", "高"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
低: "0",
|
||||
高: "1",
|
||||
}[value] || "0"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "videocfg_particle_detail",
|
||||
title: "粒子细节",
|
||||
value: ["低", "中", "高", "非常高"][parseInt(video.videocfg_particle_detail, 10)] || "低",
|
||||
options: ["低", "中", "高", "非常高"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
低: "0",
|
||||
中: "1",
|
||||
高: "2",
|
||||
非常高: "3",
|
||||
}[value] || "低"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "videocfg_ao_detail",
|
||||
title: "环境光遮蔽",
|
||||
value: ["已禁用", "中", "高"][parseInt(video.videocfg_ao_detail, 10)] || "已禁用",
|
||||
options: ["已禁用", "中", "高"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
已禁用: "0",
|
||||
中: "1",
|
||||
高: "2",
|
||||
}[value] || "已禁用"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "videocfg_hdr_detail",
|
||||
title: "高动态范围",
|
||||
value: video.videocfg_hdr_detail === "-1" ? "品质" : "性能",
|
||||
options: ["性能", "品质"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
性能: "3",
|
||||
品质: "-1",
|
||||
}[value] || "3"
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "videocfg_fsr_detail",
|
||||
title: "Fidelity FX 超级分辨率",
|
||||
value:
|
||||
["已禁用", "超高品质", "品质", "均衡", "性能"][parseInt(video.videocfg_fsr_detail, 10)] ||
|
||||
"性能",
|
||||
options: ["性能", "均衡", "品质", "超高品质", "已禁用"],
|
||||
mapping: (value: string) => {
|
||||
return (
|
||||
{
|
||||
已禁用: "0",
|
||||
超高品质: "1",
|
||||
品质: "2",
|
||||
均衡: "3",
|
||||
性能: "4",
|
||||
}[value] || "已禁用"
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// useEffect(() => {
|
||||
// setLaunchOpt(tool.state.VideoSettings[tool.state.launchIndex] || "")
|
||||
// }, [tool.state.launchIndex, tool.state.VideoSettings])
|
||||
const [vconfig, setVconfig] = useState<VideoConfig>(tool.state.videoSetting)
|
||||
|
||||
// 设置对应关系
|
||||
// TODO Value通过实际数值映射
|
||||
const videoSettings = [
|
||||
{ type: "", title: "全屏", value: "全屏", options: ["窗口", "全屏"] },
|
||||
{ type: "", title: "垂直同步", value: "关闭", options: ["关闭", "开启"] },
|
||||
{ type: "", title: "低延迟模式", value: "关闭", options: ["关闭", "开启"] },
|
||||
{ type: "", title: "增强角色对比度", value: "禁用", options: ["禁用", "启用"] },
|
||||
{ type: "", title: "CMAA2抗锯齿", value: "关闭", options: ["关闭", "开启"] },
|
||||
{
|
||||
type: "",
|
||||
title: "多重采样抗锯齿",
|
||||
value: "2X MSAA",
|
||||
options: ["无", "2X MSAA", "4X MSAA", "8X MSAA"],
|
||||
},
|
||||
{ type: "", title: "全局阴影效果", value: "低", options: ["低", "中", "高", "非常高"] },
|
||||
{ type: "", title: "动态阴影", value: "全部", options: ["仅限日光", "全部"] },
|
||||
{ type: "", title: "模型/贴图细节", value: "中", options: ["低", "中", "高"] },
|
||||
{
|
||||
type: "",
|
||||
title: "贴图过滤模式",
|
||||
value: "异向 4X",
|
||||
options: ["双线性", "三线性", "异向 2X", "异向 4X", "异向 8X", "异向 16X"],
|
||||
},
|
||||
{ type: "", title: "光影细节", value: "低", options: ["低", "高"] },
|
||||
{ type: "", title: "粒子细节", value: "低", options: ["低", "中", "高", "非常高"] },
|
||||
{ type: "", title: "环境光遮蔽", value: "已禁用", options: ["已禁用", "中", "高"] },
|
||||
{ type: "", title: "高动态范围", value: "性能", options: ["性能", "品质"] },
|
||||
{
|
||||
type: "",
|
||||
title: "Fidelity FX 超级分辨率",
|
||||
value: "已禁用",
|
||||
options: ["性能", "均衡", "品质", "超高品质", "已禁用"],
|
||||
},
|
||||
]
|
||||
useEffect(() => {
|
||||
if (steam.state.steamDirValid && steam.currentUser())
|
||||
void tool.getVideoConfig(steam.state.steamDir, steam.currentUser()?.steam_id32 || 0)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Tooltip content="功能测试中,尚未实装" showArrow={true} delay={300}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardIcon>
|
||||
<SettingConfig /> 视频设置
|
||||
</CardIcon>
|
||||
<CardTool>
|
||||
{/* {tool.state.VideoSettings.map((option, index) => (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardIcon>
|
||||
<SettingConfig /> 视频设置
|
||||
</CardIcon>
|
||||
<CardTool>
|
||||
{/* {tool.state.VideoSettings.map((option, index) => (
|
||||
<ToolButton key={index} onClick={() => tool.setLaunchIndex(index)}>
|
||||
{index + 1}
|
||||
</ToolButton>
|
||||
))} */}
|
||||
{edit && (
|
||||
{edit && (
|
||||
<>
|
||||
<ToolButton onClick={() => setVconfig({ ...vconfig, ...VideoSettingTemplate.low })}>
|
||||
低
|
||||
</ToolButton>
|
||||
<ToolButton
|
||||
onClick={() => setVconfig({ ...vconfig, ...VideoSettingTemplate.middle })}
|
||||
>
|
||||
中
|
||||
</ToolButton>
|
||||
<ToolButton onClick={() => setVconfig({ ...vconfig, ...VideoSettingTemplate.high })}>
|
||||
高
|
||||
</ToolButton>
|
||||
<ToolButton
|
||||
onClick={() => setVconfig({ ...vconfig, ...VideoSettingTemplate.veryhigh })}
|
||||
>
|
||||
非常高
|
||||
</ToolButton>
|
||||
<ToolButton
|
||||
onClick={() => setVconfig({ ...vconfig, ...VideoSettingTemplate.recommend })}
|
||||
>
|
||||
推荐
|
||||
</ToolButton>
|
||||
<ToolButton
|
||||
onClick={async () => {
|
||||
await tool.setVideoConfig(
|
||||
steam.state.steamDir,
|
||||
steam.currentUser()?.steam_id32 || 0,
|
||||
vconfig
|
||||
)
|
||||
await tool.getVideoConfig(
|
||||
steam.state.steamDir,
|
||||
steam.currentUser()?.steam_id32 || 0
|
||||
)
|
||||
setEdit(false)
|
||||
addToast({ title: "应用设置成功" })
|
||||
}}
|
||||
>
|
||||
<Plus />
|
||||
应用
|
||||
</ToolButton>
|
||||
</>
|
||||
)}
|
||||
<ToolButton
|
||||
onClick={async () => {
|
||||
if (steam.state.steamDirValid && steam.currentUser())
|
||||
await tool.getVideoConfig(
|
||||
steam.state.steamDir,
|
||||
steam.currentUser()?.steam_id32 || 0
|
||||
)
|
||||
setVconfig(tool.state.videoSetting)
|
||||
setEdit(!edit)
|
||||
}}
|
||||
>
|
||||
{edit ? (
|
||||
<>
|
||||
<ToolButton>低</ToolButton>
|
||||
<ToolButton>中</ToolButton>
|
||||
<ToolButton>高</ToolButton>
|
||||
<ToolButton>非常高</ToolButton>
|
||||
<ToolButton>推荐</ToolButton>
|
||||
<ToolButton
|
||||
onClick={() => {
|
||||
addToast({ title: "测试中 功能完成后可应用设置到游戏" })
|
||||
}}
|
||||
>
|
||||
<Plus />
|
||||
应用
|
||||
</ToolButton>
|
||||
<CloseSmall />
|
||||
取消编辑
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Edit />
|
||||
编辑
|
||||
</>
|
||||
)}
|
||||
<ToolButton onClick={() => setEdit(!edit)}>
|
||||
{edit ? (
|
||||
<>
|
||||
<CloseSmall />
|
||||
取消编辑
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Edit />
|
||||
编辑
|
||||
</>
|
||||
)}
|
||||
</ToolButton>
|
||||
<ToolButton onClick={() => setHide(!hide)}>
|
||||
{hide ? (
|
||||
<>
|
||||
<Up />
|
||||
显示
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Down />
|
||||
隐藏
|
||||
</>
|
||||
)}
|
||||
</ToolButton>
|
||||
</CardTool>
|
||||
</CardHeader>
|
||||
{!hide && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
</ToolButton>
|
||||
|
||||
<ToolButton
|
||||
onClick={async () => {
|
||||
if (steam.state.steamDirValid && steam.currentUser()) {
|
||||
await tool.getVideoConfig(
|
||||
steam.state.steamDir,
|
||||
steam.currentUser()?.steam_id32 || 0
|
||||
)
|
||||
addToast({ title: "读取成功" })
|
||||
} else addToast({ title: "请先选择用户", color: "danger" })
|
||||
}}
|
||||
>
|
||||
<CardBody>
|
||||
<ul className="flex flex-wrap gap-3 mt-1">
|
||||
<li className="flex flex-col gap-1.5">
|
||||
<span className="ml-2">分辨率</span>
|
||||
<span className="flex gap-3">
|
||||
<NumberInput
|
||||
aria-label="width"
|
||||
value={tool.state.videoSetting.width}
|
||||
onValueChange={(value) => {
|
||||
tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
width: value,
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
step={10}
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
<NumberInput
|
||||
aria-label="height"
|
||||
value={tool.state.videoSetting.height}
|
||||
onValueChange={(value) => {
|
||||
tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
height: value,
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
step={10}
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
</span>
|
||||
读取
|
||||
</ToolButton>
|
||||
<ToolButton onClick={() => setHide(!hide)}>
|
||||
{hide ? (
|
||||
<>
|
||||
<Up />
|
||||
显示
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Down />
|
||||
隐藏
|
||||
</>
|
||||
)}
|
||||
</ToolButton>
|
||||
</CardTool>
|
||||
</CardHeader>
|
||||
{!hide && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<CardBody>
|
||||
<ul className="flex flex-wrap gap-3 mt-1">
|
||||
<li className="flex flex-col gap-1.5">
|
||||
<span className="ml-2">分辨率</span>
|
||||
<span className="flex gap-3">
|
||||
<NumberInput
|
||||
aria-label="width"
|
||||
value={parseInt(
|
||||
edit ? vconfig.defaultres : tool.state.videoSetting.defaultres,
|
||||
10
|
||||
)}
|
||||
onValueChange={(value) => {
|
||||
const _ = edit
|
||||
? setVconfig({
|
||||
...vconfig,
|
||||
defaultres: value.toString(),
|
||||
})
|
||||
: tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
defaultres: value.toString(),
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
step={10}
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
<NumberInput
|
||||
aria-label="height"
|
||||
value={parseInt(edit ? vconfig.defaultresheight : tool.state.videoSetting.defaultresheight, 10)}
|
||||
onValueChange={(value) => {
|
||||
const _ = edit
|
||||
? setVconfig({
|
||||
...vconfig,
|
||||
defaultresheight: value.toString(),
|
||||
})
|
||||
: tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
defaultresheight: value.toString(),
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
step={10}
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
{videoSettings(edit ? vconfig : tool.state.videoSetting).map((vid, index) => (
|
||||
<li className="flex flex-col gap-1.5" key={index}>
|
||||
<span className="ml-2">{vid.title}</span>
|
||||
<Tabs
|
||||
size="md"
|
||||
radius="full"
|
||||
className="min-w-36"
|
||||
fullWidth
|
||||
selectedKey={vid.value}
|
||||
onSelectionChange={(key) => {
|
||||
// console.log(vid.type, key)
|
||||
// 修改 vconfig 名为 vid.type 的 value为 key
|
||||
const _ =
|
||||
edit && key
|
||||
? setVconfig({
|
||||
...vconfig,
|
||||
[vid.type]: vid.mapping(key.toString()),
|
||||
})
|
||||
: null
|
||||
}}
|
||||
>
|
||||
{vid.options.map((opt, _) => (
|
||||
<Tab key={opt} title={opt} titleValue={opt} />
|
||||
))}
|
||||
</Tabs>
|
||||
</li>
|
||||
{videoSettings.map((vid, index) => (
|
||||
<li className="flex flex-col gap-1.5" key={index}>
|
||||
<span className="ml-2">{vid.title}</span>
|
||||
<Tabs
|
||||
selectedKey={vid.value}
|
||||
size="md"
|
||||
radius="full"
|
||||
className="min-w-36"
|
||||
fullWidth
|
||||
>
|
||||
{vid.options.map((opt, _) => (
|
||||
<Tab key={opt} title={opt} />
|
||||
))}
|
||||
</Tabs>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardBody>
|
||||
</motion.div>
|
||||
)}
|
||||
</Card>
|
||||
</Tooltip>
|
||||
))}
|
||||
</ul>
|
||||
</CardBody>
|
||||
</motion.div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import { store } from "@tauri-store/valtio"
|
||||
import { useSnapshot } from "valtio"
|
||||
import { DEFAULT_STORE_CONFIG } from "./config"
|
||||
import { emit } from "@tauri-apps/api/event"
|
||||
import { send } from "process"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import VideoSetting from "@/components/cstb/VideoSetting"
|
||||
|
||||
interface LaunchOption {
|
||||
option: string
|
||||
@@ -10,24 +11,109 @@ interface LaunchOption {
|
||||
}
|
||||
|
||||
export interface VideoSetting {
|
||||
width: number; // 分辨率宽度
|
||||
height: number; // 分辨率高度
|
||||
version: string; // 版本
|
||||
vendor_id: string; // 供应商ID
|
||||
device_id: string; // 设备ID
|
||||
cpu_level: string; // CPU等级
|
||||
gpu_mem_level: string; // GPU内存等级
|
||||
gpu_level: string; // GPU等级
|
||||
knowndevice: string; // 已知设备
|
||||
defaultres: string; // 默认分辨率宽度
|
||||
defaultresheight: string; // 默认分辨率高度
|
||||
refreshrate_numerator: string; // 刷新率分子
|
||||
refreshrate_denominator: string; // 刷新率分母
|
||||
fullscreen: string; // 全屏
|
||||
vsync: string; // 垂直同步
|
||||
enhanceCharacterContrast: string; // 增强角色对比度
|
||||
cmaa2AntiAliasing: string; // CMAA2抗锯齿
|
||||
msaaAntiAliasing: string; // 多重采样抗锯齿
|
||||
globalShadowQuality: string; // 全局阴影效果
|
||||
dynamicShadows: string; // 动态阴影
|
||||
modelTextureDetail: string; // 模型/贴图细节
|
||||
textureFilteringMode: string; // 贴图过滤模式
|
||||
lightShadowDetail: string; // 光影细节
|
||||
particleDetail: string; // 粒子细节
|
||||
ambientOcclusion: string; // 环境光遮蔽
|
||||
hdr: string; // 高动态范围
|
||||
fidelityFxSuperResolution: string; // Fidelity FX 超级分辨率
|
||||
coop_fullscreen: string; // 合作模式全屏
|
||||
nowindowborder: string; // 无窗口边框
|
||||
mat_vsync: string; // 垂直同步
|
||||
fullscreen_min_on_focus_loss: string; // 失去焦点时最小化全屏
|
||||
high_dpi: string; // 高DPI
|
||||
auto_config: string; // 自动配置
|
||||
shaderquality: string; // 光影质量
|
||||
r_texturefilteringquality: string; // 纹理过滤质量
|
||||
msaa_samples: string; // 多重采样抗锯齿样本数
|
||||
r_csgo_cmaa_enable: string; // CMAA抗锯齿启用
|
||||
videocfg_shadow_quality: string; // 阴影质量
|
||||
videocfg_dynamic_shadows: string; // 动态阴影
|
||||
videocfg_texture_detail: string; // 纹理细节
|
||||
videocfg_particle_detail: string; // 粒子细节
|
||||
videocfg_ao_detail: string; // 环境光遮蔽细节
|
||||
videocfg_hdr_detail: string; // 高动态范围细节
|
||||
videocfg_fsr_detail: string; // FSR细节
|
||||
monitor_index: string; // 显示器索引
|
||||
r_low_latency: string; // 低延迟
|
||||
aspectratiomode: string; // 宽高比模式
|
||||
}
|
||||
|
||||
// 视频设置预设模版
|
||||
export const VideoSettingTemplate = {
|
||||
veryhigh: {
|
||||
shaderquality: "1",
|
||||
r_texturefilteringquality: "3",
|
||||
msaa_samples: "8",
|
||||
r_csgo_cmaa_enable: "0",
|
||||
videocfg_shadow_quality: "3",
|
||||
videocfg_dynamic_shadows: "1",
|
||||
videocfg_texture_detail: "2",
|
||||
videocfg_particle_detail: "3",
|
||||
videocfg_ao_detail: "3",
|
||||
videocfg_hdr_detail: "-1",
|
||||
videocfg_fsr_detail: "0",
|
||||
},
|
||||
high: {
|
||||
shaderquality: "1",
|
||||
r_texturefilteringquality: "3",
|
||||
msaa_samples: "4",
|
||||
r_csgo_cmaa_enable: "0",
|
||||
videocfg_shadow_quality: "2",
|
||||
videocfg_dynamic_shadows: "1",
|
||||
videocfg_texture_detail: "2",
|
||||
videocfg_particle_detail: "2",
|
||||
videocfg_ao_detail: "2",
|
||||
videocfg_hdr_detail: "-1",
|
||||
videocfg_fsr_detail: "0",
|
||||
},
|
||||
middle: {
|
||||
shaderquality: "0",
|
||||
r_texturefilteringquality: "1",
|
||||
msaa_samples: "2",
|
||||
r_csgo_cmaa_enable: "0",
|
||||
videocfg_shadow_quality: "1",
|
||||
videocfg_dynamic_shadows: "1",
|
||||
videocfg_texture_detail: "1",
|
||||
videocfg_particle_detail: "1",
|
||||
videocfg_ao_detail: "0",
|
||||
videocfg_fsr_detail: "2",
|
||||
},
|
||||
low: {
|
||||
shaderquality: "0",
|
||||
r_texturefilteringquality: "0",
|
||||
msaa_samples: "0",
|
||||
r_csgo_cmaa_enable: "0",
|
||||
videocfg_shadow_quality: "0",
|
||||
videocfg_dynamic_shadows: "0",
|
||||
videocfg_texture_detail: "0",
|
||||
videocfg_particle_detail: "0",
|
||||
videocfg_ao_detail: "0",
|
||||
videocfg_hdr_detail: "3",
|
||||
videocfg_fsr_detail: "3",
|
||||
},
|
||||
recommend: {
|
||||
shaderquality: "0",
|
||||
r_texturefilteringquality: "3",
|
||||
msaa_samples: "2",
|
||||
r_csgo_cmaa_enable: "0",
|
||||
videocfg_shadow_quality: "0",
|
||||
videocfg_dynamic_shadows: "1",
|
||||
videocfg_texture_detail: "1",
|
||||
videocfg_particle_detail: "0",
|
||||
videocfg_ao_detail: "0",
|
||||
videocfg_hdr_detail: "3",
|
||||
videocfg_fsr_detail: "0",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
const defaultValue = {
|
||||
launchOptions: [
|
||||
{ option: "-novid -high -freq 144 -fullscreen", name: "" },
|
||||
@@ -37,8 +123,38 @@ const defaultValue = {
|
||||
launchIndex: 0,
|
||||
powerPlan: 0,
|
||||
videoSetting: {
|
||||
width: 1920,
|
||||
height: 1080
|
||||
version: "15",
|
||||
vendor_id: "0",
|
||||
device_id: "0",
|
||||
cpu_level: "3",
|
||||
gpu_mem_level: "3",
|
||||
gpu_level: "3",
|
||||
knowndevice: "0",
|
||||
defaultres: "1920",
|
||||
defaultresheight: "1080",
|
||||
refreshrate_numerator: "144",
|
||||
refreshrate_denominator: "1",
|
||||
fullscreen: "1",
|
||||
coop_fullscreen: "0",
|
||||
nowindowborder: "1",
|
||||
mat_vsync: "0",
|
||||
fullscreen_min_on_focus_loss: "1",
|
||||
high_dpi: "0",
|
||||
auto_config: "2",
|
||||
shaderquality: "0",
|
||||
r_texturefilteringquality: "3",
|
||||
msaa_samples: "2",
|
||||
r_csgo_cmaa_enable: "0",
|
||||
videocfg_shadow_quality: "0",
|
||||
videocfg_dynamic_shadows: "1",
|
||||
videocfg_texture_detail: "1",
|
||||
videocfg_particle_detail: "0",
|
||||
videocfg_ao_detail: "0",
|
||||
videocfg_hdr_detail: "3",
|
||||
videocfg_fsr_detail: "0",
|
||||
monitor_index: "0",
|
||||
r_low_latency: "1",
|
||||
aspectratiomode: "0",
|
||||
} as VideoSetting,
|
||||
}
|
||||
|
||||
@@ -67,6 +183,8 @@ export const useToolStore = () => {
|
||||
setLaunchIndex,
|
||||
setPowerPlan,
|
||||
setVideoSetting,
|
||||
getVideoConfig,
|
||||
setVideoConfig,
|
||||
addLaunchOption,
|
||||
resetToolStore,
|
||||
}
|
||||
@@ -104,6 +222,17 @@ const setVideoSetting = (setting: VideoSetting) => {
|
||||
toolStore.state.videoSetting = setting
|
||||
}
|
||||
|
||||
const getVideoConfig = async (steam_dir: string, steam_id32: number) => {
|
||||
const video = await invoke<VideoSetting>("get_cs2_video_config", { steamDir: steam_dir, steamId32: steam_id32 })
|
||||
// console.log(video)
|
||||
setVideoSetting(video)
|
||||
}
|
||||
|
||||
const setVideoConfig = async (steam_dir: string, steam_id32: number, video_config: VideoSetting) => {
|
||||
console.log(video_config.videocfg_hdr_detail)
|
||||
await invoke("set_cs2_video_config", { steamDir: steam_dir, steamId32: steam_id32, videoConfig: video_config })
|
||||
}
|
||||
|
||||
const addLaunchOption = (option: LaunchOption) => {
|
||||
// 限制最高10个
|
||||
if (toolStore.state.launchOptions.length >= 10) {
|
||||
|
||||
Reference in New Issue
Block a user