1use std::{
2 fs::File,
3 io::{Error, Read, Seek},
4 path::PathBuf,
5 sync::LazyLock,
6};
7
8use anyhow::Context;
9use serde::{Deserialize, Deserializer, Serialize};
10
11#[cfg(target_os = "windows")]
12const AMP_PATH: &str = "%Temp%\\VRChat\\VRChat\\amplitude.cache";
13
14#[cfg(target_os = "linux")]
15const AMP_PATH: &str = "$HOME/.local/share/Steam/steamapps/compatdata/438100/pfx/drive_c/users/steamuser/AppData/Local/Temp/VRChat/VRChat/amplitude.cache";
16
17#[cfg(target_os = "windows")]
18const LOW_PATH: &str = "%AppData%\\..\\LocalLow\\VRChat\\VRChat";
19
20#[cfg(target_os = "linux")]
21const LOW_PATH: &str = "$HOME/.local/share/Steam/steamapps/compatdata/438100/pfx/drive_c/users/steamuser/AppData/LocalLow/VRChat/VRChat";
22
23pub static VRCHAT_AMP_PATH: LazyLock<PathBuf> =
24 LazyLock::new(|| crate::parse_path_env(AMP_PATH).expect("Failed to parse amplitude path"));
25
26pub static VRCHAT_LOW_PATH: LazyLock<PathBuf> =
28 LazyLock::new(|| crate::parse_path_env(LOW_PATH).expect("Failed to parse local low path"));
29
30#[derive(Clone, Debug, Deserialize, Serialize)]
31pub struct VRChat {
32 #[serde(deserialize_with = "deserialize")]
37 pub cache_directory: PathBuf,
38}
39
40pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<PathBuf, D::Error> {
45 let haystack = String::deserialize(deserializer)?;
46 let path = crate::parse_path_env(&haystack)
47 .context("Failed to parse the default path")
48 .map_err(serde::de::Error::custom)?
49 .join("Cache-WindowsPlayer");
50
51 Ok(path)
52}
53
54impl VRChat {
55 #[must_use]
56 pub fn get_path() -> PathBuf {
57 VRCHAT_LOW_PATH.join("config.json")
58 }
59
60 pub fn load() -> Result<Self, Error> {
65 let path = Self::get_path();
66 let mut file = File::options()
67 .read(true)
68 .write(true)
69 .create(true)
70 .truncate(false)
71 .open(path)?;
72
73 let mut text = String::new();
74 file.read_to_string(&mut text)?;
75 file.rewind()?;
76
77 serde_json::from_str(&text).map_or_else(|_| Ok(Self::default()), Ok)
79 }
80}
81
82impl Default for VRChat {
83 fn default() -> Self {
84 Self {
85 cache_directory: VRCHAT_LOW_PATH.join("Cache-WindowsPlayer"),
86 }
87 }
88}