Resolve virtual path (res://foo/bar.png or user://save.dat) to actual location
(path: &str)
| 293 | Excluded(String), |
| 294 | Disk(PathBuf), |
| 295 | WebUserStorage(String), |
| 296 | PerroAssets(String), |
| 297 | StaticBinary(String), |
| 298 | DlcStaticBinary { dlc: String, path: String }, |
| 299 | DlcPerroAssets { dlc: String, virtual_path: String }, |
| 300 | } |
| 301 | |
| 302 | fn normalize_user_app_name(name: &str) -> String { |
| 303 | name.replace(' ', "_") |
| 304 | } |
| 305 | |
| 306 | fn user_app_name(project_root_opt: &Option<ProjectRoot>) -> String { |
| 307 | let app_name = project_root_opt |
| 308 | .as_ref() |
| 309 | .map(|root| match root { |
| 310 | ProjectRoot::Disk { name, .. } => name.as_str(), |
| 311 | ProjectRoot::PerroAssets { name, .. } => name.as_str(), |
| 312 | }) |
| 313 | .expect("Project root not set"); |
| 314 | normalize_user_app_name(app_name) |
| 315 | } |
| 316 | |
| 317 | #[cfg(any(test, target_arch = "wasm32"))] |
| 318 | fn user_storage_key(app_name: &str, relative_path: &str) -> String { |
| 319 | format!("perro:user:{app_name}:data:{relative_path}") |
| 320 | } |
| 321 | |
| 322 | pub fn validate_virtual_asset_path(path: &str) -> io::Result<()> { |
| 323 | if let Some(stripped) = path.strip_prefix("user://") { |
| 324 | return validate_asset_relative_path(stripped); |
| 325 | } |
| 326 | |
| 327 | if let Some(stripped) = path.strip_prefix("res://") { |
| 328 | return validate_asset_relative_path(stripped); |
| 329 | } |
| 330 | |
| 331 | if let Some(rest) = path.strip_prefix("dlc://") { |
| 332 | let (mount_raw, rel_raw) = rest.split_once('/').unwrap_or((rest, "")); |
| 333 | validate_dlc_mount_name(mount_raw)?; |
| 334 | return validate_asset_relative_path(rel_raw.trim_start_matches('/')); |
| 335 | } |
| 336 | |
| 337 | let path_buf = Path::new(path); |
| 338 | if path_buf.is_absolute() { |
| 339 | return Ok(()); |
| 340 | } |
| 341 | validate_asset_relative_path(path) |
| 342 | } |
| 343 | |
| 344 | pub fn validate_asset_relative_path(path: &str) -> io::Result<()> { |
| 345 | if path.contains('\\') { |
| 346 | return Err(io::Error::new( |
| 347 | io::ErrorKind::InvalidInput, |
| 348 | "asset paths must use `/` separators", |
| 349 | )); |
| 350 | } |
| 351 | if Path::new(path).is_absolute() { |
| 352 | return Err(io::Error::new( |