2024-05-26 07:23:21 -04:00
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
use std ::env ::home_dir ;
use std ::fs ;
use std ::fs ::read_dir ;
2024-06-28 06:10:37 -04:00
use std ::path ::PathBuf ;
2024-05-26 07:23:21 -04:00
/// Where the existing installation came from
#[ derive(Clone, Copy) ]
#[ repr(C) ]
pub enum ExistingInstallType {
/// Installed via the official launcher
OfficialLauncher ,
/// Installed via XIVQuickLauncher
XIVQuickLauncher ,
/// Installed via XIVLauncherCore
XIVLauncherCore ,
/// Installed via XIVOnMac
XIVOnMac ,
/// Installed via Astra
Astra
}
/// An existing install location on disk
pub struct ExistingGameDirectory {
/// The application where this installation was from
pub install_type : ExistingInstallType ,
/// The path to the "main folder" where "game" and "boot" sits
pub path : String
}
/// Finds existing installations on disk. Will only return locations that actually have files in them, and a really basic check to see if the data is valid.
pub fn find_existing_game_dirs ( ) -> Vec < ExistingGameDirectory > {
let mut install_dirs = Vec ::new ( ) ;
match std ::env ::consts ::OS {
" linux " = > {
// Official install (Wine)
install_dirs . push ( ExistingGameDirectory {
install_type : ExistingInstallType ::OfficialLauncher ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " .wine/drive_c/Program Files (x86)/SquareEnix/FINAL FANTASY XIV - A Realm Reborn " )
2024-05-26 07:23:21 -04:00
} ) ;
// Official install (Steam)
install_dirs . push ( ExistingGameDirectory {
install_type : ExistingInstallType ::OfficialLauncher ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " .steam/steam/steamapps/common/FINAL FANTASY XIV - A Realm Reborn " )
2024-05-26 07:23:21 -04:00
} ) ;
// XIVLauncherCore location
install_dirs . push ( ExistingGameDirectory {
install_type : ExistingInstallType ::XIVLauncherCore ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " .xlcore/ffxiv " )
2024-05-26 07:23:21 -04:00
} ) ;
// Astra location. But we have to iterate through each UUID.
2024-05-26 07:56:20 -04:00
if let Ok ( entries ) = read_dir ( from_home_dir ( " .local/share/astra/game/ " ) ) {
2024-05-26 07:23:21 -04:00
entries
. flatten ( )
. flat_map ( | entry | {
let Ok ( meta ) = entry . metadata ( ) else {
return vec! [ ] ;
} ;
if meta . is_dir ( ) {
return vec! [ entry . path ( ) ] ;
}
vec! [ ]
} )
. for_each ( | path | {
install_dirs . push ( ExistingGameDirectory {
install_type : ExistingInstallType ::Astra ,
path : path . into_os_string ( ) . into_string ( ) . unwrap ( )
} )
} ) ;
}
}
" macos " = > {
// Official Launcher (macOS)
install_dirs . push ( ExistingGameDirectory {
install_type : ExistingInstallType ::OfficialLauncher ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " Library/Application Support/FINAL FANTASY XIV ONLINE/Bottles/published_Final_Fantasy/drive_c/Program Files (x86)/SquareEnix/FINAL FANTASY XIV - A Realm Reborn " )
2024-05-26 07:23:21 -04:00
} ) ;
// TODO: add XIV on Mac
}
" windows " = > {
// Official install (Wine)
install_dirs . push ( ExistingGameDirectory {
install_type : ExistingInstallType ::OfficialLauncher ,
path : " C: \\ Program Files (x86) \\ SquareEnix \\ FINAL FANTASY XIV - A Realm Reborn " . parse ( ) . unwrap ( )
} ) ;
// TODO: Add Astra
}
& _ = > { }
}
2024-05-26 07:49:55 -04:00
install_dirs . into_iter ( ) . filter ( | dir | is_valid_game_dir ( & dir . path ) ) . collect ( )
2024-05-26 07:23:21 -04:00
}
/// An existing user directory
pub struct ExistingUserDirectory {
/// The application where this directory was from
pub install_type : ExistingInstallType ,
/// The path to the user folder
pub path : String
}
/// Finds existing user folders on disk. Will only return locations that actually have files in them, and a really basic check to see if the data is valid.
pub fn find_existing_user_dirs ( ) -> Vec < ExistingUserDirectory > {
let mut user_dirs = Vec ::new ( ) ;
let Some ( home_dir ) = home_dir ( ) else {
return user_dirs ;
} ;
match std ::env ::consts ::OS {
" linux " = > {
// Official install (Wine)
user_dirs . push ( ExistingUserDirectory {
install_type : ExistingInstallType ::OfficialLauncher ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " Documents/My Games/FINAL FANTASY XIV - A Realm Reborn " )
2024-05-26 07:23:21 -04:00
} ) ;
// XIVLauncherCore location
user_dirs . push ( ExistingUserDirectory {
install_type : ExistingInstallType ::XIVLauncherCore ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " .xlcore/ffxivConfig " )
2024-05-26 07:23:21 -04:00
} ) ;
// Astra location. But we have to iterate through each UUID.
2024-05-26 07:56:20 -04:00
if let Ok ( entries ) = read_dir ( from_home_dir ( " .local/share/astra/user/ " ) ) {
2024-05-26 07:23:21 -04:00
entries
. flatten ( )
. flat_map ( | entry | {
let Ok ( meta ) = entry . metadata ( ) else {
return vec! [ ] ;
} ;
if meta . is_dir ( ) {
return vec! [ entry . path ( ) ] ;
}
vec! [ ]
} )
. for_each ( | path | {
user_dirs . push ( ExistingUserDirectory {
install_type : ExistingInstallType ::Astra ,
path : path . into_os_string ( ) . into_string ( ) . unwrap ( )
} )
} ) ;
}
}
" macos " = > {
// Official install (Wine)
user_dirs . push ( ExistingUserDirectory {
install_type : ExistingInstallType ::OfficialLauncher ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " Documents/My Games/FINAL FANTASY XIV - A Realm Reborn " )
2024-05-26 07:23:21 -04:00
} )
// TODO: Add XIV on Mac?
}
" windows " = > {
// Official install
user_dirs . push ( ExistingUserDirectory {
install_type : ExistingInstallType ::OfficialLauncher ,
2024-05-26 07:56:20 -04:00
path : from_home_dir ( " Documents/My Games/FINAL FANTASY XIV - A Realm Reborn " )
2024-05-26 07:23:21 -04:00
} )
// TODO: Add Astra
}
& _ = > { }
}
2024-05-26 07:49:55 -04:00
user_dirs . into_iter ( ) . filter ( | dir | is_valid_user_dir ( & dir . path ) ) . collect ( )
2024-05-26 07:23:21 -04:00
}
fn from_home_dir ( path : & 'static str ) -> String {
let mut new_path = home_dir ( ) . unwrap ( ) ;
2024-05-26 07:56:20 -04:00
new_path . push ( path ) ;
2024-05-26 07:23:21 -04:00
return new_path . into_os_string ( ) . into_string ( ) . unwrap ( ) ;
}
fn is_valid_game_dir ( path : & String ) -> bool {
let mut d = PathBuf ::from ( path ) ;
// Check for the dir itself
if fs ::metadata ( d . as_path ( ) ) . is_err ( ) {
return false ;
}
// Check for "game"
d . push ( " game " ) ;
if fs ::metadata ( d . as_path ( ) ) . is_err ( ) {
return false ;
}
// Check for "boot"
d . pop ( ) ;
d . push ( " boot " ) ;
if fs ::metadata ( d . as_path ( ) ) . is_err ( ) {
return false ;
}
true
}
fn is_valid_user_dir ( path : & String ) -> bool {
let mut d = PathBuf ::from ( path ) ;
// Check for the dir itself
if fs ::metadata ( d . as_path ( ) ) . is_err ( ) {
return false ;
}
// Check for "FFXIV.cfg"
d . push ( " FFXIV.cfg " ) ;
if fs ::metadata ( d . as_path ( ) ) . is_err ( ) {
return false ;
}
true
}