use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use windows_sys::core::GUID;
use windows_sys::Win32::Foundation::{
GetLastError, ERROR_INSUFFICIENT_BUFFER, MAX_PATH, PWSTR, S_OK,
};
use windows_sys::Win32::System::Com::CoTaskMemFree;
use windows_sys::Win32::System::LibraryLoader::GetModuleFileNameW;
use windows_sys::Win32::UI::Shell::{
FOLDERID_Documents, FOLDERID_Downloads, FOLDERID_LocalAppData, FOLDERID_Profile,
FOLDERID_RoamingAppData, SHGetKnownFolderPath,
};
fn get_windows_path(folder: GUID) -> Option<PathBuf> {
unsafe {
let mut str: PWSTR = std::ptr::null_mut();
let res = SHGetKnownFolderPath(&folder, 0, std::ptr::null_mut(), &mut str as _);
if res != S_OK {
return None;
}
debug_assert_eq!(str.align_offset(2), 0);
let mut count: usize = 0;
while std::ptr::read(str.add(count)) != 0 {
count += 1;
}
let slice = std::slice::from_raw_parts(str, count);
let str1 = OsString::from_wide(&slice);
CoTaskMemFree(str as _);
Some(str1.into())
}
}
pub fn get_app_cache() -> Option<PathBuf> {
get_windows_path(FOLDERID_LocalAppData)
}
pub fn get_app_config() -> Option<PathBuf> {
None }
pub fn get_app_data() -> Option<PathBuf> {
get_windows_path(FOLDERID_RoamingAppData)
}
pub fn get_app_logs() -> Option<PathBuf> {
None }
pub fn get_app_documents() -> Option<PathBuf> {
None }
fn get_exe_path() -> Option<PathBuf> {
unsafe {
let mut buf: [u16; MAX_PATH as usize] = [0; MAX_PATH as usize];
let res = GetModuleFileNameW(0, &mut buf as _, MAX_PATH);
if res == 0 {
return None; } else if res == MAX_PATH {
let err = GetLastError();
if err == ERROR_INSUFFICIENT_BUFFER {
let mut len = MAX_PATH as usize * 2;
loop {
let mut v = Vec::with_capacity(len);
let res = GetModuleFileNameW(0, v.as_mut_ptr(), len as u32);
if res == 0 {
return None; } else if res == len as u32 {
let err = GetLastError();
if err != ERROR_INSUFFICIENT_BUFFER {
break;
}
} else {
break;
}
len *= 2;
}
}
}
let str1 = OsString::from_wide(&buf[..res as usize]);
Some(str1.into())
}
}
pub fn get_app_bundled_asset(file_name: &str) -> Option<PathBuf> {
let assets = get_exe_path()?.parent()?.join("Assets");
let file = assets.join(file_name);
Some(file)
}
pub fn get_user_home() -> Option<PathBuf> {
get_windows_path(FOLDERID_Profile)
}
pub fn get_user_documents() -> Option<PathBuf> {
get_windows_path(FOLDERID_Documents)
}
pub fn get_user_downloads() -> Option<PathBuf> {
get_windows_path(FOLDERID_Downloads)
}