1use std::path::{Path, PathBuf};
11use std::sync::Mutex;
12
13static BACKUP_ROOT: Mutex<Option<PathBuf>> = Mutex::new(None);
16
17pub fn set_backup_root(path: PathBuf) {
19 if let Ok(mut root) = BACKUP_ROOT.lock() {
20 *root = Some(path);
21 }
22}
23
24pub fn get_backup_root() -> PathBuf {
26 BACKUP_ROOT
27 .lock()
28 .ok()
29 .and_then(|r| r.clone())
30 .unwrap_or_else(|| {
31 dirs::home_dir()
32 .map(|h| h.join("NotAlterra"))
33 .unwrap_or_else(exe_dir)
34 })
35}
36
37pub fn sentinel_path() -> PathBuf {
39 config_base_dir().join("NOTALTERRA_LICENSE_ACCEPTED")
40}
41
42pub fn disclaimer_accepted() -> bool {
44 sentinel_path().exists()
45}
46
47pub fn accept_disclaimer() -> std::io::Result<()> {
49 std::fs::write(sentinel_path(), [])?;
50 Ok(())
51}
52
53pub fn reject_disclaimer() -> std::io::Result<()> {
55 let p = sentinel_path();
56 if p.exists() {
57 std::fs::remove_file(p)?;
58 }
59 Ok(())
60}
61
62pub fn stale_config_path() -> PathBuf {
64 exe_dir().join("config.ini")
65}
66
67pub fn cleanup_stale_config() -> bool {
69 let path = stale_config_path();
70 if path.exists() {
71 std::fs::remove_file(&path).ok();
72 true
73 } else {
74 false
75 }
76}
77
78pub fn backups_saves_dir() -> PathBuf {
81 let p = get_backup_root().join("backups").join("saves");
82 std::fs::create_dir_all(&p).ok();
83 p
84}
85
86pub fn backups_config_dir() -> PathBuf {
89 let p = get_backup_root().join("backups").join("ue5");
90 std::fs::create_dir_all(&p).ok();
91 p
92}
93
94fn home_notalterra_dir() -> PathBuf {
97 dirs::home_dir()
98 .map(|h| h.join("NotAlterra"))
99 .unwrap_or_else(exe_dir)
100}
101
102fn config_base_dir() -> PathBuf {
108 dirs::data_local_dir()
109 .map(|d| d.join("NotAlterra").join("config"))
110 .unwrap_or_else(exe_dir)
111}
112
113pub fn app_ini_path() -> PathBuf {
115 let p = config_base_dir().join("app.ini");
116 std::fs::create_dir_all(p.parent().unwrap_or(Path::new("."))).ok();
117 p
118}
119
120#[derive(Debug, Default)]
122pub struct AppConfig {
123 pub save_folder: Option<String>,
124 pub backup_root: Option<String>,
125}
126
127pub fn load_app_config() -> AppConfig {
130 let path = app_ini_path();
131 if !path.exists() {
132 return AppConfig::default();
133 }
134 let content = match std::fs::read_to_string(&path) {
135 Ok(c) => c,
136 Err(_) => return AppConfig::default(),
137 };
138 let mut cfg = AppConfig::default();
139 for line in content.lines() {
140 let line = line.trim();
141 if line.is_empty() || line.starts_with('#') || line.starts_with(';') {
142 continue;
143 }
144 if let Some((key, value)) = line.split_once('=') {
145 let key = key.trim();
146 let val = value.trim();
147 match key {
148 "save_folder" => cfg.save_folder = Some(val.to_string()),
149 "backup_root" => cfg.backup_root = Some(val.to_string()),
150 _ => {}
151 }
152 }
153 }
154 cfg
155}
156
157pub fn save_app_config(save_folder: Option<&str>, backup_root: Option<&str>) {
159 let path = app_ini_path();
160 let mut content = String::from(
161 "# NotAlterra configuration\n\
162 # This file is auto-generated. Edit while the tool is not running.\n\n",
163 );
164 if let Some(s) = save_folder {
165 content.push_str(&format!("save_folder = {s}\n"));
166 }
167 if let Some(r) = backup_root {
168 content.push_str(&format!("backup_root = {r}\n"));
169 }
170 let _ = std::fs::write(&path, content);
171}
172
173pub fn ensure_dir(path: PathBuf) {
175 let _ = std::fs::create_dir_all(&path);
176}
177
178pub fn exe_dir() -> PathBuf {
180 std::env::current_exe()
181 .ok()
182 .and_then(|p| p.parent().map(Path::to_path_buf))
183 .unwrap_or_else(|| PathBuf::from("."))
184}