2025-03-23 21:55:17 +08:00
|
|
|
use anyhow::Result;
|
|
|
|
|
use base64::engine::general_purpose::STANDARD;
|
|
|
|
|
use base64::Engine;
|
2025-03-27 15:29:12 +08:00
|
|
|
use regex::Regex;
|
2025-03-23 21:55:17 +08:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use serde_json::Value;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::fs;
|
|
|
|
|
use std::path::Path;
|
2025-03-23 22:41:19 +08:00
|
|
|
use tauri_plugin_http::reqwest::blocking::get;
|
2025-03-23 21:55:17 +08:00
|
|
|
use walkdir::WalkDir;
|
|
|
|
|
|
|
|
|
|
use crate::steam;
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
|
pub struct User {
|
|
|
|
|
steam_id64: u64,
|
|
|
|
|
steam_id32: u32,
|
|
|
|
|
account_name: String,
|
|
|
|
|
persona_name: String,
|
|
|
|
|
recent: i32,
|
|
|
|
|
avatar: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
|
pub struct LoginUser {
|
|
|
|
|
steam_id64: u64,
|
|
|
|
|
steam_id32: u32,
|
|
|
|
|
account_name: String,
|
|
|
|
|
persona_name: String,
|
|
|
|
|
remember_password: String,
|
|
|
|
|
wants_offline_mode: String,
|
|
|
|
|
skip_offline_mode_warning: String,
|
|
|
|
|
allow_auto_login: String,
|
|
|
|
|
most_recent: String,
|
|
|
|
|
timestamp: String,
|
|
|
|
|
avatar: String,
|
|
|
|
|
avatar_key: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
|
pub struct LocalUser {
|
|
|
|
|
steam_id32: u32,
|
|
|
|
|
persona_name: String,
|
|
|
|
|
avatar_key: String,
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-27 11:30:03 +08:00
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
|
pub struct VideoConfig {
|
|
|
|
|
version: String,
|
|
|
|
|
vendor_id: String,
|
|
|
|
|
device_id: String,
|
2025-03-27 13:32:30 +08:00
|
|
|
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,
|
2025-03-27 11:30:03 +08:00
|
|
|
auto_config: String,
|
2025-03-27 13:32:30 +08:00
|
|
|
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 {
|
2025-03-27 15:29:12 +08:00
|
|
|
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(),
|
|
|
|
|
}
|
2025-03-27 13:32:30 +08:00
|
|
|
}
|
2025-03-27 11:30:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-23 21:55:17 +08:00
|
|
|
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() {
|
|
|
|
|
return Ok(Vec::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let data = fs::read_to_string(t_path)?;
|
|
|
|
|
let json_data = super::parse::to_json(&data);
|
|
|
|
|
let kv: HashMap<String, Value> = serde_json::from_str(&json_data)?;
|
|
|
|
|
|
|
|
|
|
let mut users = Vec::new();
|
|
|
|
|
for (k, v) in kv {
|
|
|
|
|
let props = v.as_object().unwrap();
|
|
|
|
|
|
|
|
|
|
let avatar = if let Some(img) = read_avatar(&steam_dir, &k) {
|
|
|
|
|
img
|
|
|
|
|
} else {
|
|
|
|
|
String::new()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let id64 = k.parse::<u64>()?;
|
|
|
|
|
|
|
|
|
|
let user = LoginUser {
|
|
|
|
|
steam_id32: steam::id::id64_to_32(id64),
|
|
|
|
|
steam_id64: id64,
|
|
|
|
|
account_name: props
|
|
|
|
|
.get("AccountName")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
persona_name: props
|
|
|
|
|
.get("PersonaName")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
remember_password: props
|
|
|
|
|
.get("RememberPassword")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
wants_offline_mode: props
|
|
|
|
|
.get("WantsOfflineMode")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
skip_offline_mode_warning: props
|
|
|
|
|
.get("SkipOfflineModeWarning")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
allow_auto_login: props
|
|
|
|
|
.get("AllowAutoLogin")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
most_recent: props
|
|
|
|
|
.get("MostRecent")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
timestamp: props
|
|
|
|
|
.get("Timestamp")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
avatar,
|
|
|
|
|
avatar_key: String::new(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
users.push(user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(users)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn parse_local_users(steam_dir: &str) -> Result<Vec<LocalUser>> {
|
|
|
|
|
let root = Path::new(steam_dir).join("userdata");
|
|
|
|
|
if !root.exists() {
|
|
|
|
|
return Ok(Vec::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut local_users = Vec::new();
|
|
|
|
|
|
|
|
|
|
for entry in WalkDir::new(&root) {
|
|
|
|
|
let entry = entry?;
|
|
|
|
|
let path = entry.path();
|
|
|
|
|
|
|
|
|
|
// 跳过根目录
|
|
|
|
|
if path == root {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 只处理目录
|
|
|
|
|
if entry.file_type().is_dir() {
|
|
|
|
|
let id = path.file_name().unwrap().to_str().unwrap();
|
|
|
|
|
|
|
|
|
|
// 检查 localconfig.vdf 文件是否存在
|
|
|
|
|
let local_config_path = path.join("config/localconfig.vdf");
|
|
|
|
|
if !local_config_path.exists() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 读取并解析 localconfig.vdf 文件
|
|
|
|
|
let data = fs::read_to_string(local_config_path)?;
|
|
|
|
|
let json_data = super::parse::to_json(&data);
|
|
|
|
|
let kv: HashMap<String, Value> = serde_json::from_str(&json_data)?;
|
|
|
|
|
|
2025-03-27 15:29:12 +08:00
|
|
|
// 剥离顶层 UserLocalConfigStore
|
2025-03-27 16:11:42 +08:00
|
|
|
// let kv = kv.get("UserLocalConfigStore").and_then(|v| v.as_object()).unwrap();
|
2025-03-27 15:29:12 +08:00
|
|
|
|
2025-03-23 21:55:17 +08:00
|
|
|
// 获取 friends 节点
|
|
|
|
|
let friends = kv.get("friends").and_then(|v| v.as_object());
|
|
|
|
|
if friends.is_none() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let friends = friends.unwrap();
|
|
|
|
|
|
|
|
|
|
// 获取 PersonaName
|
|
|
|
|
let persona_name = friends
|
|
|
|
|
.get("PersonaName")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
// 获取 AvatarKey
|
|
|
|
|
let avatar_key = friends
|
|
|
|
|
.get(id)
|
|
|
|
|
.and_then(|v| v.as_object())
|
|
|
|
|
.and_then(|props| props.get("avatar"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
// 创建 LocalUser 并加入列表
|
|
|
|
|
local_users.push(LocalUser {
|
|
|
|
|
steam_id32: id.parse::<u32>().unwrap(),
|
|
|
|
|
persona_name,
|
|
|
|
|
avatar_key,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 跳过子目录
|
|
|
|
|
WalkDir::new(path).max_depth(1).into_iter().next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(local_users)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn merge_users(login: Vec<LoginUser>, local: Vec<LocalUser>) -> Vec<User> {
|
|
|
|
|
let mut users = Vec::new();
|
|
|
|
|
for i in login {
|
2025-03-23 22:41:19 +08:00
|
|
|
let avatar: String;
|
2025-03-23 21:55:17 +08:00
|
|
|
let mut avatar_key = String::new();
|
|
|
|
|
|
2025-03-23 22:41:19 +08:00
|
|
|
// 匹配获取 avatar_key 在线获取头像使用
|
2025-03-23 21:55:17 +08:00
|
|
|
let t_usr: Vec<&LocalUser> = local
|
|
|
|
|
.iter()
|
2025-03-23 22:41:19 +08:00
|
|
|
.filter(|j| i.steam_id32 == j.steam_id32)
|
2025-03-23 21:55:17 +08:00
|
|
|
.collect();
|
2025-03-23 22:41:19 +08:00
|
|
|
|
|
|
|
|
if let Some(usr) = t_usr.first() {
|
|
|
|
|
avatar_key = usr.avatar_key.clone();
|
2025-03-23 21:55:17 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-23 22:41:19 +08:00
|
|
|
// 获取头像的base64 本地头像文件不存在时使用在线api获取
|
|
|
|
|
if i.avatar.is_empty() && !avatar_key.is_empty() {
|
2025-03-23 21:55:17 +08:00
|
|
|
avatar = download_avatar(&avatar_key).unwrap_or_default();
|
2025-03-23 22:41:19 +08:00
|
|
|
} else {
|
|
|
|
|
avatar = i.avatar;
|
2025-03-23 21:55:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
users.push(User {
|
|
|
|
|
steam_id64: i.steam_id64,
|
2025-03-23 22:41:19 +08:00
|
|
|
steam_id32: i.steam_id32,
|
2025-03-23 21:55:17 +08:00
|
|
|
account_name: i.account_name,
|
|
|
|
|
persona_name: i.persona_name,
|
|
|
|
|
recent: i.most_recent.parse().unwrap_or(0),
|
|
|
|
|
avatar,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 把第一个recent=1的放在头部
|
|
|
|
|
for (id, usr) in users.iter_mut().enumerate() {
|
|
|
|
|
if usr.recent == 1 {
|
|
|
|
|
let tmp = users.remove(id);
|
|
|
|
|
users.insert(0, tmp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
users
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-23 22:41:19 +08:00
|
|
|
pub fn get_users(steam_dir: &str) -> Result<Vec<User>> {
|
|
|
|
|
let login_users = parse_login_users(steam_dir)?;
|
|
|
|
|
let local_users = parse_local_users(steam_dir)?;
|
|
|
|
|
let users = merge_users(login_users, local_users);
|
|
|
|
|
|
|
|
|
|
Ok(users)
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-23 21:55:17 +08:00
|
|
|
fn download_avatar(avatar_key: &str) -> Result<String> {
|
2025-03-23 22:41:19 +08:00
|
|
|
// 下载并转换成Base64 "https://avatars.cloudflare.steamstatic.com/" + avatarKey + "_full.jpg"
|
|
|
|
|
let url = format!(
|
|
|
|
|
"https://avatars.cloudflare.steamstatic.com/{}_full.jpg",
|
|
|
|
|
avatar_key
|
|
|
|
|
);
|
|
|
|
|
if let Ok(resp) = get(&url) {
|
|
|
|
|
if let Ok(bytes) = resp.bytes() {
|
|
|
|
|
return Ok(STANDARD.encode(bytes));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Err(anyhow::anyhow!("Failed to download avatar"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn read_avatar(steam_dir: &str, steam_id64: &str) -> Option<String> {
|
|
|
|
|
let t_path = Path::new(steam_dir).join(format!("config\\avatarcache\\{}.png", steam_id64));
|
|
|
|
|
if !t_path.exists() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Ok(img) = fs::read(t_path) {
|
|
|
|
|
Some(STANDARD.encode(img))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-27 13:32:30 +08:00
|
|
|
pub fn get_cs2_video(file_path: &str) -> Result<VideoConfig> {
|
|
|
|
|
// TODO: no kv
|
|
|
|
|
let data = fs::read_to_string(file_path)?;
|
2025-03-27 11:30:03 +08:00
|
|
|
let json_data = super::parse::to_json(&data);
|
|
|
|
|
let kv: HashMap<String, String> = serde_json::from_str(&json_data)?;
|
2025-03-27 13:32:30 +08:00
|
|
|
let video_config = VideoConfig {
|
2025-03-27 17:36:04 +08:00
|
|
|
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(),
|
2025-03-27 13:32:30 +08:00
|
|
|
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(),
|
2025-03-27 17:36:04 +08:00
|
|
|
auto_config: kv.get("AutoConfig").unwrap_or(&"".to_string()).to_string(),
|
2025-03-27 13:32:30 +08:00
|
|
|
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)
|
2025-03-27 11:30:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-27 13:32:30 +08:00
|
|
|
pub fn set_cs2_video(file_path: &str, data: VideoConfig) -> Result<()> {
|
2025-03-27 15:29:12 +08:00
|
|
|
// 读取文件内容
|
|
|
|
|
let file_content = fs::read_to_string(file_path)?;
|
|
|
|
|
|
|
|
|
|
// 定义正则表达式匹配模式
|
2025-03-27 18:02:51 +08:00
|
|
|
let re = Regex::new(r#""(setting\.\w+)"\s+"-?\d+""#).unwrap();
|
2025-03-27 15:29:12 +08:00
|
|
|
|
|
|
|
|
// 替换字段值
|
|
|
|
|
let updated_content = re.replace_all(&file_content, |caps: ®ex::Captures| {
|
|
|
|
|
let key = &caps[1]; // 捕获的键名
|
|
|
|
|
let value = match key {
|
2025-03-27 17:36:04 +08:00
|
|
|
"Version" => &data.version,
|
|
|
|
|
"VendorID" => &data.vendor_id,
|
|
|
|
|
"DeviceID" => &data.device_id,
|
2025-03-27 15:29:12 +08:00
|
|
|
"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,
|
2025-03-27 17:36:04 +08:00
|
|
|
"AutoConfig" => &data.auto_config,
|
2025-03-27 15:29:12 +08:00
|
|
|
"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())?;
|
2025-03-27 11:30:03 +08:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-23 22:41:19 +08:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_get_users() {
|
|
|
|
|
let users = get_users("D:\\Programs\\Steam").unwrap();
|
|
|
|
|
println!("{:?}", users);
|
|
|
|
|
}
|
2025-03-27 11:30:03 +08:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_get_cs2_video() {
|
2025-03-27 13:32:30 +08:00
|
|
|
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();
|
2025-03-27 11:30:03 +08:00
|
|
|
println!("{:?}", video_config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_set_cs2_video() {
|
2025-03-27 13:32:30 +08:00
|
|
|
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
|
|
|
|
let file_path = format!("{}/temp/cs2_video.txt", manifest_dir);
|
2025-03-27 15:29:12 +08:00
|
|
|
fs::copy(
|
|
|
|
|
format!("{}/src/vdf/tests/cs2_video.txt", manifest_dir),
|
|
|
|
|
file_path.clone(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-03-27 13:32:30 +08:00
|
|
|
let video_config = VideoConfig::default();
|
|
|
|
|
set_cs2_video(&file_path, video_config).unwrap();
|
2025-03-27 11:30:03 +08:00
|
|
|
}
|
2025-03-23 21:55:17 +08:00
|
|
|
}
|