From 3fea1b319a3d4199b502ebf7c054e9b4ffde6344 Mon Sep 17 00:00:00 2001 From: p0358 Date: Mon, 26 Jan 2026 21:47:19 +0100 Subject: [PATCH 1/2] separate cache dir from Maxima data dir --- maxima-lib/src/content/downloader.rs | 4 ++-- maxima-lib/src/content/manager.rs | 6 +++--- maxima-lib/src/core/mod.rs | 4 ++-- maxima-lib/src/unix/wine.rs | 16 ++++++++-------- maxima-lib/src/util/native.rs | 25 +++++++++++++++++++++++++ maxima-ui/src/bridge/get_games.rs | 8 ++++---- maxima-ui/src/ui_image.rs | 6 +++--- 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/maxima-lib/src/content/downloader.rs b/maxima-lib/src/content/downloader.rs index e982a19..c373974 100644 --- a/maxima-lib/src/content/downloader.rs +++ b/maxima-lib/src/content/downloader.rs @@ -16,7 +16,7 @@ use crate::{ }, util::{ hash::hash_file_crc32, - native::{maxima_dir, NativeError, SafeParent, SafeStr}, + native::{maxima_cache_dir, NativeError, SafeParent, SafeStr}, }, }; use async_compression::tokio::write::DeflateDecoder; @@ -37,7 +37,7 @@ use tokio::{ use tokio_util::compat::FuturesAsyncReadCompatExt; fn zstate_path(id: &str, path: &str) -> Result { - let mut path = maxima_dir()?.join("temp/downloader").join(id).join(path); + let mut path = maxima_cache_dir()?.join("downloader").join(id).join(path); path.set_extension("eazstate"); std::fs::create_dir_all(path.safe_parent()?)?; Ok(path) diff --git a/maxima-lib/src/content/manager.rs b/maxima-lib/src/content/manager.rs index f16116d..669f7d7 100644 --- a/maxima-lib/src/content/manager.rs +++ b/maxima-lib/src/content/manager.rs @@ -28,7 +28,7 @@ use crate::{ service_layer::ServiceLayerError, MaximaEvent, }, - util::native::{maxima_dir, NativeError}, + util::native::{maxima_cache_dir, NativeError}, }; const QUEUE_FILE: &str = "download_queue.json"; @@ -93,7 +93,7 @@ pub enum DownloaderError { impl DownloadQueue { pub(crate) async fn load() -> Result { - let file = maxima_dir()?.join(QUEUE_FILE); + let file = maxima_cache_dir()?.join(QUEUE_FILE); if !file.exists() { return Ok(Self::default()); } @@ -108,7 +108,7 @@ impl DownloadQueue { } pub(crate) async fn save(&self) -> Result<(), ContentManagerError> { - let file = maxima_dir()?.join(QUEUE_FILE); + let file = maxima_cache_dir()?.join(QUEUE_FILE); fs::write(file, serde_json::to_string(&self)?).await?; Ok(()) } diff --git a/maxima-lib/src/core/mod.rs b/maxima-lib/src/core/mod.rs index 88ff044..bb80607 100644 --- a/maxima-lib/src/core/mod.rs +++ b/maxima-lib/src/core/mod.rs @@ -66,7 +66,7 @@ use crate::{ content::manager::{ContentManager, ContentManagerError}, lsx::{self, service::LSXServerError, types::LSXRequestType}, rtm::client::{BasicPresence, RtmClient}, - util::native::{maxima_dir, NativeError}, + util::native::{maxima_cache_dir, NativeError}, }; #[derive(Clone, IntoStaticStr)] @@ -401,7 +401,7 @@ impl Maxima { width: u16, height: u16, ) -> Result { - let dir = maxima_dir()?.join("cache/avatars"); + let dir = maxima_cache_dir()?.join("avatars"); create_dir_all(&dir)?; Ok(dir.join(format!("{}_{}x{}.jpg", id, width, height))) diff --git a/maxima-lib/src/unix/wine.rs b/maxima-lib/src/unix/wine.rs index 50c61a6..ea32baf 100644 --- a/maxima-lib/src/unix/wine.rs +++ b/maxima-lib/src/unix/wine.rs @@ -24,7 +24,7 @@ use xz2::read::XzDecoder; use crate::util::{ github::{fetch_github_release, fetch_github_releases, github_download_asset, GithubRelease}, - native::{maxima_dir, DownloadError, NativeError, SafeParent, SafeStr, WineError}, + native::{maxima_dir, maxima_cache_dir, DownloadError, NativeError, SafeParent, SafeStr, WineError}, registry::RegistryError, }; @@ -315,19 +315,19 @@ pub(crate) async fn install_wine() -> Result<(), NativeError> { None => return Err(NativeError::Wine(WineError::Fetch)), }; - let dir = maxima_dir()?.join("downloads"); + let dir = maxima_cache_dir()?.join("downloads"); create_dir_all(&dir)?; - let path = dir.join(&asset.name); - github_download_asset(asset, &path)?; - extract_wine(&path)?; + let archive_path = dir.join(&asset.name); + github_download_asset(asset, &archive_path)?; + extract_wine(&archive_path)?; let mut versions = versions()?; versions.proton = release.tag_name; set_versions(versions)?; - if let Err(err) = remove_file(&path) { - warn!("Failed to delete {:?} - {:?}", path, err); + if let Err(err) = remove_file(&archive_path) { + warn!("Failed to delete {:?} - {:?}", archive_path, err); } let _ = run_wine_command("", None::<[&str; 0]>, None, false, CommandType::Run).await; @@ -413,7 +413,7 @@ pub async fn setup_wine_registry() -> Result<(), NativeError> { } } - let path = maxima_dir()?.join("temp").join("wine.reg"); + let path = maxima_cache_dir()?.join("wine.reg"); tokio::fs::create_dir_all(path.safe_parent()?).await?; { diff --git a/maxima-lib/src/util/native.rs b/maxima-lib/src/util/native.rs index 5adfc60..e7da52c 100644 --- a/maxima-lib/src/util/native.rs +++ b/maxima-lib/src/util/native.rs @@ -258,6 +258,31 @@ pub fn maxima_dir() -> Result { Ok(path) } +#[cfg(not(unix))] +pub fn maxima_cache_dir() -> Result { + use directories::ProjectDirs; + + let dirs = ProjectDirs::from("com", "ArmchairDevelopers", "Maxima"); + let path = dirs.unwrap().cache_dir().to_path_buf(); + create_dir_all(&path)?; + Ok(path) +} + +#[cfg(unix)] +pub fn maxima_cache_dir() -> Result { + let home = if let Ok(home) = env::var("XDG_CACHE_HOME") { + home + } else if let Ok(home) = env::var("HOME") { + format!("{}/.cache", home) + } else { + return Err(NativeError::MissingEnvironmentVariable("HOME".to_string())); + }; + + let path = PathBuf::from(format!("{}/maxima", home)); + create_dir_all(&path)?; + Ok(path) +} + #[cfg(unix)] pub fn platform_path>(path: P) -> PathBuf { PathBuf::from(format!("Z:{}", path.as_ref().to_str().unwrap())) diff --git a/maxima-ui/src/bridge/get_games.rs b/maxima-ui/src/bridge/get_games.rs index f2a62ca..e212d2f 100644 --- a/maxima-ui/src/bridge/get_games.rs +++ b/maxima-ui/src/bridge/get_games.rs @@ -14,7 +14,7 @@ use maxima::{ }, LockedMaxima, }, - util::native::maxima_dir, + util::native::maxima_cache_dir, }; use std::{fs, sync::mpsc::Sender}; @@ -245,9 +245,9 @@ pub async fn get_games_request( }); channel.send(res)?; - let bg = maxima_dir()?.join("cache/ui/images/").join(&slug).join("background.jpg"); - let game_hero = maxima_dir()?.join("cache/ui/images/").join(&slug).join("hero.jpg"); - let game_logo = maxima_dir()?.join("cache/ui/images/").join(&slug).join("logo.png"); + let bg = maxima_cache_dir()?.join("ui/images/").join(&slug).join("background.jpg"); + let game_hero = maxima_cache_dir()?.join("ui/images/").join(&slug).join("hero.jpg"); + let game_logo = maxima_cache_dir()?.join("ui/images/").join(&slug).join("logo.png"); let has_hero = fs::metadata(&game_hero).is_ok(); let has_logo = fs::metadata(&game_logo).is_ok(); let has_background = fs::metadata(&bg).is_ok(); diff --git a/maxima-ui/src/ui_image.rs b/maxima-ui/src/ui_image.rs index 58e40e7..7108793 100644 --- a/maxima-ui/src/ui_image.rs +++ b/maxima-ui/src/ui_image.rs @@ -16,7 +16,7 @@ use core::slice::SlicePattern; use log::{debug, error, info}; use image::io::Reader as ImageReader; -use maxima::util::native::{maxima_dir, NativeError, SafeStr}; +use maxima::util::native::{maxima_cache_dir, NativeError, SafeStr}; #[derive(Clone, PartialEq, Eq, Hash, std::fmt::Debug)] pub enum UIImageType { @@ -101,9 +101,9 @@ impl UIImageCache { } fn get_path_for_image(variant: &UIImageType) -> Result { - let image_cache_root = maxima_dir()?.join("cache/ui/images"); + let image_cache_root = maxima_cache_dir()?.join("ui/images"); // lib should probably create the pfp path but we'll check both just in case we're first - let avatar_cache_root = maxima_dir()?.join("cache/avatars"); + let avatar_cache_root = maxima_cache_dir()?.join("avatars"); match variant { UIImageType::Hero(slug) => Ok(image_cache_root.join(&slug).join("hero.jpg")), UIImageType::Logo(slug) => Ok(image_cache_root.join(&slug).join("logo.png")), From 61889b437e930a7a40430ab794ae4044e0723cac Mon Sep 17 00:00:00 2001 From: p0358 Date: Wed, 4 Feb 2026 20:53:02 +0100 Subject: [PATCH 2/2] rely on ProjectDirs for maxima dirs on all platforms, since they line up with what we had on Linux (includes unit tests to ensure that's the case) --- maxima-lib/src/util/native.rs | 107 +++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/maxima-lib/src/util/native.rs b/maxima-lib/src/util/native.rs index e7da52c..57a11ed 100644 --- a/maxima-lib/src/util/native.rs +++ b/maxima-lib/src/util/native.rs @@ -223,9 +223,7 @@ pub fn module_path() -> Result { #[cfg(target_os = "linux")] pub fn module_path() -> Result { - let path = std::fs::read_link("/proc/self/exe"); - - Ok(path?) + Ok(std::fs::read_link("/proc/self/exe")?) } #[cfg(target_os = "macos")] @@ -233,7 +231,6 @@ pub fn module_path() -> Result { Ok(env::current_exe()?) } -#[cfg(not(unix))] pub fn maxima_dir() -> Result { use directories::ProjectDirs; @@ -243,22 +240,6 @@ pub fn maxima_dir() -> Result { Ok(path) } -#[cfg(unix)] -pub fn maxima_dir() -> Result { - let home = if let Ok(home) = env::var("XDG_DATA_HOME") { - home - } else if let Ok(home) = env::var("HOME") { - format!("{}/.local/share", home) - } else { - return Err(NativeError::MissingEnvironmentVariable("HOME".to_string())); - }; - - let path = PathBuf::from(format!("{}/maxima", home)); - create_dir_all(&path)?; - Ok(path) -} - -#[cfg(not(unix))] pub fn maxima_cache_dir() -> Result { use directories::ProjectDirs; @@ -268,21 +249,6 @@ pub fn maxima_cache_dir() -> Result { Ok(path) } -#[cfg(unix)] -pub fn maxima_cache_dir() -> Result { - let home = if let Ok(home) = env::var("XDG_CACHE_HOME") { - home - } else if let Ok(home) = env::var("HOME") { - format!("{}/.cache", home) - } else { - return Err(NativeError::MissingEnvironmentVariable("HOME".to_string())); - }; - - let path = PathBuf::from(format!("{}/maxima", home)); - create_dir_all(&path)?; - Ok(path) -} - #[cfg(unix)] pub fn platform_path>(path: P) -> PathBuf { PathBuf::from(format!("Z:{}", path.as_ref().to_str().unwrap())) @@ -292,3 +258,74 @@ pub fn platform_path>(path: P) -> PathBuf { pub fn platform_path>(path: P) -> PathBuf { PathBuf::from(path.as_ref()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_maxima_dir() -> Result<(), NativeError> { + let dir_expected: PathBuf = if cfg!(target_os = "linux") { + let home = if let Ok(home) = env::var("XDG_DATA_HOME") { + home + } else if let Ok(home) = env::var("HOME") { + format!("{}/.local/share", home) + } else { + return Err(NativeError::MissingEnvironmentVariable("HOME".to_string())); + }; + PathBuf::from(format!("{}/maxima", home)) + } else if cfg!(target_os = "macos") { + if let Ok(home) = env::var("HOME") { + PathBuf::from(format!("{}/Library/Application Support/com.ArmchairDevelopers.Maxima", home)) + } else { + return Err(NativeError::MissingEnvironmentVariable("HOME".to_string())); + } + } else if cfg!(target_os = "windows") { + if let Ok(appdata) = env::var("APPDATA") { + PathBuf::from(format!("{}/ArmchairDevelopers/Maxima/data", appdata)) + } else { + return Err(NativeError::MissingEnvironmentVariable("APPDATA".to_string())); + } + } else { + panic!("unsupported platform"); + }; + + let dir = maxima_dir().unwrap(); + println!("[test_maxima_dir] Expected: {:?} Got: {:?}", dir_expected, dir); + assert_eq!(dir, dir_expected); + Ok(()) + } + + #[test] + fn test_maxima_cache_dir() -> Result<(), NativeError> { + let dir_expected: PathBuf = if cfg!(target_os = "linux") { + let home = if let Ok(home) = env::var("XDG_CACHE_HOME") { + home + } else if let Ok(home) = env::var("HOME") { + format!("{}/.cache", home) + } else { + return Err(NativeError::MissingEnvironmentVariable("HOME".to_string())); + }; + PathBuf::from(format!("{}/maxima", home)) + } else if cfg!(target_os = "macos") { + if let Ok(home) = env::var("HOME") { + PathBuf::from(format!("{}/Library/Caches/com.ArmchairDevelopers.Maxima", home)) + } else { + return Err(NativeError::MissingEnvironmentVariable("HOME".to_string())); + } + } else if cfg!(target_os = "windows") { + if let Ok(localappdata) = env::var("LOCALAPPDATA") { + PathBuf::from(format!("{}/ArmchairDevelopers/Maxima/cache", localappdata)) + } else { + return Err(NativeError::MissingEnvironmentVariable("LOCALAPPDATA".to_string())); + } + } else { + panic!("unsupported platform"); + }; + + let dir = maxima_cache_dir().unwrap(); + println!("[test_maxima_cache_dir] Expected: {:?} Got: {:?}", dir_expected, dir); + assert_eq!(dir, dir_expected); + Ok(()) + } +}