2021-11-01 09:54:58 -04:00
# include <QPushButton>
# include <QProcess>
# include <QNetworkAccessManager>
# include <QNetworkReply>
# include <QUrlQuery>
# include <QDir>
# include <QFormLayout>
# include <QLineEdit>
# include <QRegularExpression>
# include <QComboBox>
# include <QJsonObject>
# include <QJsonDocument>
# include <QCheckBox>
2022-03-01 17:21:29 -05:00
# include <keychain.h>
2021-11-01 09:54:58 -04:00
# include <QMessageBox>
2021-11-01 13:14:00 -04:00
# include <QMenuBar>
2021-11-10 05:18:59 -05:00
# include <QCoreApplication>
2021-11-23 14:37:37 -05:00
# include <QStandardPaths>
# include <QRegularExpressionMatch>
2022-03-27 21:08:27 -04:00
# include <algorithm>
2022-04-06 13:18:40 -04:00
# include <QTcpServer>
2021-11-09 20:57:10 -05:00
2021-11-23 15:34:23 -05:00
# include "launchercore.h"
2021-11-01 09:54:58 -04:00
# include "sapphirelauncher.h"
# include "squarelauncher.h"
# include "squareboot.h"
2021-11-01 13:14:00 -04:00
# include "settingswindow.h"
2021-11-09 15:02:17 -05:00
# include "blowfish.h"
2021-11-23 14:37:37 -05:00
# include "assetupdater.h"
2022-02-25 20:29:21 -05:00
# include "encryptedarg.h"
2022-01-27 09:25:23 -05:00
# ifdef ENABLE_WATCHDOG
2021-12-06 21:15:31 -05:00
# include "watchdog.h"
2022-01-27 09:25:23 -05:00
# endif
2021-11-01 09:54:58 -04:00
2021-11-23 15:34:23 -05:00
void LauncherCore : : setSSL ( QNetworkRequest & request ) {
2021-11-01 09:54:58 -04:00
QSslConfiguration config ;
config . setProtocol ( QSsl : : AnyProtocol ) ;
config . setPeerVerifyMode ( QSslSocket : : VerifyNone ) ;
request . setSslConfiguration ( config ) ;
}
2022-03-16 15:03:35 -04:00
void LauncherCore : : buildRequest ( const ProfileSettings & settings , QNetworkRequest & request ) {
2021-11-01 09:54:58 -04:00
setSSL ( request ) ;
2022-03-16 15:03:35 -04:00
if ( settings . license = = GameLicense : : macOS ) {
request . setHeader ( QNetworkRequest : : UserAgentHeader , " macSQEXAuthor/2.0.0(MacOSX; ja-jp) " ) ;
} else {
request . setHeader ( QNetworkRequest : : UserAgentHeader ,
QString ( " SQEXAuthor/2.0.0(Windows 6.2; ja-jp; %1) " ) . arg ( QString ( QSysInfo : : bootUniqueId ( ) ) ) ) ;
}
2021-11-01 09:54:58 -04:00
request . setRawHeader ( " Accept " ,
" image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, application/x-ms-xbap, */* " ) ;
request . setRawHeader ( " Accept-Encoding " , " gzip, deflate " ) ;
request . setRawHeader ( " Accept-Language " , " en-us " ) ;
}
2021-11-23 15:34:23 -05:00
void LauncherCore : : launchGame ( const ProfileSettings & profile , const LoginAuth auth ) {
2021-11-02 08:36:30 -04:00
QList < QString > arguments ;
2021-11-23 14:37:37 -05:00
QString dataDir = QStandardPaths : : writableLocation ( QStandardPaths : : AppDataLocation ) ;
2022-03-13 19:58:58 -04:00
if ( profile . dalamud . enabled ) {
2021-11-23 14:37:37 -05:00
arguments . push_back ( dataDir + " /NativeLauncher.exe " ) ;
2022-04-06 13:18:40 -04:00
arguments . push_back ( " 5248 " ) ; // TODO: make port configurable/random
2021-11-23 14:37:37 -05:00
}
2021-11-02 08:36:30 -04:00
// now for the actual game...
2021-11-23 15:34:23 -05:00
if ( profile . useDX9 ) {
arguments . push_back ( profile . gamePath + " \\ game \\ ffxiv.exe " ) ;
2021-11-02 08:49:06 -04:00
} else {
2021-11-23 15:34:23 -05:00
arguments . push_back ( profile . gamePath + " \\ game \\ ffxiv_dx11.exe " ) ;
2021-11-02 08:49:06 -04:00
}
2021-11-09 21:26:16 -05:00
struct Argument {
QString key , value ;
} ;
2021-11-02 08:36:30 -04:00
2021-11-09 21:26:16 -05:00
QList < Argument > gameArgs ;
gameArgs . push_back ( { " DEV.DataPathType " , QString : : number ( 1 ) } ) ;
gameArgs . push_back ( { " DEV.UseSqPack " , QString : : number ( 1 ) } ) ;
gameArgs . push_back ( { " DEV.MaxEntitledExpansionID " , QString : : number ( auth . maxExpansion ) } ) ;
gameArgs . push_back ( { " DEV.TestSID " , auth . SID } ) ;
gameArgs . push_back ( { " SYS.Region " , QString : : number ( auth . region ) } ) ;
2021-11-23 15:34:23 -05:00
gameArgs . push_back ( { " language " , QString : : number ( profile . language ) } ) ;
gameArgs . push_back ( { " ver " , profile . gameVersion } ) ;
2021-11-02 08:36:30 -04:00
if ( ! auth . lobbyhost . isEmpty ( ) ) {
2021-11-09 21:26:16 -05:00
gameArgs . push_back ( { " DEV.GMServerHost " , auth . frontierHost } ) ;
for ( int i = 1 ; i < 9 ; i + + ) {
gameArgs . push_back ( { QString ( " DEV.LobbyHost0%1 " ) . arg ( QString : : number ( i ) ) , auth . lobbyhost } ) ;
gameArgs . push_back ( { QString ( " DEV.LobbyPort0%1 " ) . arg ( QString : : number ( i ) ) , QString : : number ( 54994 ) } ) ;
}
2021-11-02 08:36:30 -04:00
}
2021-11-23 14:37:37 -05:00
auto gameProcess = new QProcess ( this ) ;
2022-02-25 20:06:29 -05:00
QProcessEnvironment env = QProcessEnvironment : : systemEnvironment ( ) ;
2022-03-16 09:59:27 -04:00
if ( profile . license = = GameLicense : : WindowsSteam ) {
2022-01-30 16:57:44 -05:00
gameArgs . push_back ( { " IsSteam " , " 1 " } ) ;
2022-02-25 20:06:29 -05:00
env . insert ( " IS_FFXIV_LAUNCH_FROM_STEAM " , QString : : number ( 1 ) ) ;
2022-01-30 16:57:44 -05:00
}
2022-03-16 14:33:04 -04:00
if ( profile . dalamud . enabled ) {
// TODO: this depends on the default wine Z: path existing, which may not
// always the case.
env . insert ( " DALAMUD_RUNTIME " ,
" Z: " + dataDir . replace ( ' / ' , ' \\ ' ) + " \\ DalamudRuntime " ) ;
}
2022-02-25 20:06:29 -05:00
gameProcess - > setProcessEnvironment ( env ) ;
2022-04-06 13:18:40 -04:00
gameProcess - > setProcessChannelMode ( QProcess : : ForwardedChannels ) ;
2021-11-23 14:37:37 -05:00
2022-03-28 20:24:24 +08:00
const QString argFormat = profile . encryptArguments ? " /%1 =%2 " : " %1=%2 " ;
2021-11-09 15:02:17 -05:00
2022-01-27 11:12:23 -05:00
QString argJoined ;
for ( const auto & arg : gameArgs ) {
argJoined + = argFormat . arg ( arg . key , arg . value ) ;
}
2021-11-09 21:26:16 -05:00
2022-01-27 11:12:23 -05:00
if ( profile . encryptArguments ) {
arguments . append ( encryptGameArg ( argJoined ) ) ;
} else {
arguments . append ( argJoined ) ;
2021-11-09 15:02:17 -05:00
}
2022-01-27 11:12:23 -05:00
2022-04-06 13:18:40 -04:00
if ( profile . dalamud . enabled ) {
auto socket = new QTcpServer ( ) ;
connect ( socket , & QTcpServer : : newConnection , [ this , profile , socket ] {
auto connection = socket - > nextPendingConnection ( ) ;
connect ( connection , & QTcpSocket : : readyRead , [ this , connection , profile , socket ] {
QString output = connection - > readAll ( ) ;
bool success ;
int exitCode = output . toInt ( & success , 10 ) ;
if ( exitCode ! = - 1 & & success ) {
QString dataDir = QStandardPaths : : writableLocation ( QStandardPaths : : AppDataLocation ) ;
dataDir = " Z: " + dataDir . replace ( ' / ' , ' \\ ' ) ;
QJsonObject startInfo ;
startInfo [ " WorkingDirectory " ] = dataDir ;
startInfo [ " ConfigurationPath " ] = dataDir + " \\ dalamudConfig.json " ;
startInfo [ " PluginDirectory " ] = dataDir + " \\ installedPlugins " ;
startInfo [ " AssetDirectory " ] = dataDir + " \\ DalamudAssets " ;
startInfo [ " DefaultPluginDirectory " ] = dataDir + " \\ devPlugins " ;
startInfo [ " DelayInitializeMs " ] = 0 ;
startInfo [ " GameVersion " ] = profile . gameVersion ;
startInfo [ " Language " ] = profile . language ;
startInfo [ " OptOutMbCollection " ] = profile . dalamud . optOutOfMbCollection ;
QString argsEncoded = QJsonDocument ( startInfo ) . toJson ( ) . toBase64 ( ) ;
auto dalamudProcess = new QProcess ( ) ;
dalamudProcess - > setProcessChannelMode ( QProcess : : ForwardedChannels ) ;
QProcessEnvironment env = QProcessEnvironment : : systemEnvironment ( ) ;
env . insert ( " DALAMUD_RUNTIME " , dataDir + " \\ DalamudRuntime " ) ;
# if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
env . insert ( " XL_WINEONLINUX " , " true " ) ;
# endif
dalamudProcess - > setProcessEnvironment ( env ) ;
auto list = dalamudProcess - > processEnvironment ( ) . toStringList ( ) ;
2022-04-09 17:53:43 -04:00
launchExecutable ( profile , dalamudProcess , { dataDir + " /Dalamud/ " + " Dalamud.Injector.exe " , QString : : number ( exitCode ) , argsEncoded } , false ) ;
2022-04-06 13:18:40 -04:00
connection - > close ( ) ;
socket - > close ( ) ;
}
2022-02-24 09:10:00 -05:00
} ) ;
2022-04-06 13:18:40 -04:00
} ) ;
socket - > listen ( QHostAddress : : Any , 5248 ) ;
}
2022-02-24 09:10:00 -05:00
2022-02-25 20:20:52 -05:00
launchGameExecutable ( profile , gameProcess , arguments ) ;
2022-02-24 09:10:00 -05:00
successfulLaunch ( ) ;
2021-11-02 08:36:30 -04:00
}
2021-11-23 15:34:23 -05:00
void LauncherCore : : launchExecutable ( const ProfileSettings & profile , const QStringList args ) {
2021-11-01 09:54:58 -04:00
auto process = new QProcess ( this ) ;
2022-02-25 20:20:52 -05:00
process - > setProcessEnvironment ( QProcessEnvironment : : systemEnvironment ( ) ) ;
2022-04-09 17:53:43 -04:00
launchExecutable ( profile , process , args , true ) ;
}
void LauncherCore : : launchExternalTool ( const ProfileSettings & profile , const QStringList args ) {
auto process = new QProcess ( this ) ;
process - > setProcessEnvironment ( QProcessEnvironment : : systemEnvironment ( ) ) ;
launchExecutable ( profile , process , args , false ) ;
2021-11-23 14:37:37 -05:00
}
2021-11-01 09:54:58 -04:00
2022-02-25 20:20:52 -05:00
void LauncherCore : : launchGameExecutable ( const ProfileSettings & profile , QProcess * process , const QStringList args ) {
2021-11-01 14:35:32 -04:00
QList < QString > arguments ;
2021-11-02 20:04:53 -04:00
2022-02-25 22:25:39 -05:00
arguments . append ( args ) ;
2022-04-09 17:53:43 -04:00
launchExecutable ( profile , process , arguments , true ) ;
2022-02-25 22:25:39 -05:00
}
2022-04-09 17:53:43 -04:00
void LauncherCore : : launchExecutable ( const ProfileSettings & profile , QProcess * process , const QStringList args , bool isGame ) {
2022-02-25 22:25:39 -05:00
QList < QString > arguments ;
auto env = process - > processEnvironment ( ) ;
2021-11-01 09:54:58 -04:00
# if defined(Q_OS_LINUX)
2022-04-09 17:53:43 -04:00
if ( isGame ) {
if ( profile . useGamescope ) {
arguments . push_back ( " gamescope " ) ;
2022-02-23 21:18:53 -05:00
2022-04-09 17:53:43 -04:00
if ( profile . gamescope . fullscreen )
arguments . push_back ( " -f " ) ;
2022-02-23 21:18:53 -05:00
2022-04-09 17:53:43 -04:00
if ( profile . gamescope . borderless )
arguments . push_back ( " -b " ) ;
2022-02-23 21:18:53 -05:00
2022-04-09 17:53:43 -04:00
if ( profile . gamescope . width > 0 )
arguments . push_back ( " -w " +
QString : : number ( profile . gamescope . width ) ) ;
2022-02-23 21:18:53 -05:00
2022-04-09 17:53:43 -04:00
if ( profile . gamescope . height > 0 )
arguments . push_back ( " -h " +
QString : : number ( profile . gamescope . height ) ) ;
2022-02-23 21:18:53 -05:00
2022-04-09 17:53:43 -04:00
if ( profile . gamescope . refreshRate > 0 )
arguments . push_back (
" -r " + QString : : number ( profile . gamescope . refreshRate ) ) ;
}
2021-11-01 09:54:58 -04:00
2022-04-09 17:53:43 -04:00
if ( profile . useGamemode )
arguments . push_back ( " gamemoderun " ) ;
}
2022-02-25 20:20:52 -05:00
# endif
# if defined(Q_OS_LINUX)
2022-02-24 08:41:37 -05:00
if ( profile . useEsync ) {
2022-02-25 20:06:29 -05:00
env . insert ( " WINEESYNC " , QString : : number ( 1 ) ) ;
env . insert ( " WINEFSYNC " , QString : : number ( 1 ) ) ;
env . insert ( " WINEFSYNC_FUTEX2 " , QString : : number ( 1 ) ) ;
2022-02-24 08:41:37 -05:00
}
2021-11-03 06:18:31 -04:00
# endif
2021-11-01 09:54:58 -04:00
2021-11-03 06:31:02 -04:00
# if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
2022-02-25 20:06:29 -05:00
env . insert ( " WINEPREFIX " , profile . winePrefixPath ) ;
2021-11-01 14:35:32 -04:00
2021-11-23 15:34:23 -05:00
arguments . push_back ( profile . winePath ) ;
2021-11-03 06:31:02 -04:00
# endif
2021-11-02 08:36:30 -04:00
arguments . append ( args ) ;
2021-11-01 09:54:58 -04:00
2021-11-01 14:35:32 -04:00
auto executable = arguments [ 0 ] ;
arguments . removeFirst ( ) ;
2021-11-03 06:18:31 -04:00
2022-04-09 17:53:43 -04:00
if ( isGame )
process - > setWorkingDirectory ( profile . gamePath + " /game/ " ) ;
2022-02-25 20:06:29 -05:00
process - > setProcessEnvironment ( env ) ;
2021-11-23 14:37:37 -05:00
2022-02-25 20:20:52 -05:00
qDebug ( ) < < " launching " < < executable < < " with args " < < arguments ;
2021-11-01 14:35:32 -04:00
process - > start ( executable , arguments ) ;
2021-11-01 09:54:58 -04:00
}
2021-11-23 15:34:23 -05:00
QString LauncherCore : : readVersion ( QString path ) {
2021-11-01 09:54:58 -04:00
QFile file ( path ) ;
file . open ( QFile : : OpenModeFlag : : ReadOnly ) ;
return file . readAll ( ) ;
}
2021-11-23 15:34:23 -05:00
void LauncherCore : : readInitialInformation ( ) {
2021-11-09 12:25:54 -05:00
defaultProfileIndex = settings . value ( " defaultProfile " , 0 ) . toInt ( ) ;
2022-04-10 16:58:29 -04:00
auto defaultAppSettings = AppSettings ( ) ;
appSettings . closeWhenLaunched = settings . value ( " closeWhenLaunched " , defaultAppSettings . closeWhenLaunched ) . toBool ( ) ;
appSettings . showBanners = settings . value ( " showBanners " , defaultAppSettings . showBanners ) . toBool ( ) ;
appSettings . showNewsList = settings . value ( " showNewsList " , defaultAppSettings . showNewsList ) . toBool ( ) ;
2022-02-24 09:10:00 -05:00
2022-02-24 08:35:31 -05:00
gamescopeAvailable = checkIfInPath ( " gamescope " ) ;
gamemodeAvailable = checkIfInPath ( " gamemoderun " ) ;
2022-02-25 22:25:21 -05:00
const QString dataDir =
QStandardPaths : : writableLocation ( QStandardPaths : : AppDataLocation ) ;
const bool hasDalamud = QFile : : exists ( dataDir + " /Dalamud " ) ;
if ( hasDalamud ) {
if ( QFile : : exists ( dataDir + " /Dalamud/Dalamud.deps.json " ) ) {
QFile depsJson ( dataDir + " /Dalamud/Dalamud.deps.json " ) ;
depsJson . open ( QFile : : ReadOnly ) ;
QJsonDocument doc = QJsonDocument : : fromJson ( depsJson . readAll ( ) ) ;
// TODO: UGLY
QString versionString =
doc [ " targets " ]
. toObject ( ) [ " .NETCoreApp,Version=v5.0 " ]
. toObject ( )
. keys ( )
. filter ( " Dalamud " ) [ 0 ] ;
dalamudVersion = versionString . remove ( " Dalamud/ " ) ;
}
if ( QFile : : exists ( dataDir + " /DalamudAssets/asset.ver " ) ) {
QFile assetJson ( dataDir + " /DalamudAssets/asset.ver " ) ;
assetJson . open ( QFile : : ReadOnly | QFile : : Text ) ;
dalamudAssetVersion = QString ( assetJson . readAll ( ) ) . toInt ( ) ;
}
if ( QFile : : exists ( dataDir + " /DalamudRuntime/runtime.ver " ) ) {
QFile runtimeVer ( dataDir + " /DalamudRuntime/runtime.ver " ) ;
runtimeVer . open ( QFile : : ReadOnly | QFile : : Text ) ;
runtimeVersion = QString ( runtimeVer . readAll ( ) ) ;
}
}
2022-04-08 19:34:51 -04:00
if ( QFile : : exists ( dataDir + " /nativelauncher.ver " ) ) {
QFile nativeVer ( dataDir + " /nativelauncher.ver " ) ;
nativeVer . open ( QFile : : ReadOnly | QFile : : Text ) ;
nativeLauncherVersion = QString ( nativeVer . readAll ( ) ) ;
}
2021-11-09 11:25:15 -05:00
auto profiles = settings . childGroups ( ) ;
// create the Default profile if it doesnt exist
if ( profiles . empty ( ) )
2021-11-09 13:26:59 -05:00
profiles . append ( QUuid : : createUuid ( ) . toString ( QUuid : : StringFormat : : WithoutBraces ) ) ;
2021-11-09 11:25:15 -05:00
2021-11-09 13:24:38 -05:00
profileSettings . resize ( profiles . size ( ) ) ;
for ( const auto & uuid : profiles ) {
2021-11-09 11:25:15 -05:00
ProfileSettings profile ;
2021-11-09 13:24:38 -05:00
profile . uuid = QUuid ( uuid ) ;
settings . beginGroup ( uuid ) ;
2021-11-09 11:25:15 -05:00
2021-11-09 13:26:59 -05:00
profile . name = settings . value ( " name " , " Default " ) . toString ( ) ;
2021-11-09 11:25:15 -05:00
2021-11-10 04:30:01 -05:00
readWineInfo ( profile ) ;
2021-11-03 06:18:31 -04:00
2021-11-09 11:25:15 -05:00
if ( settings . contains ( " gamePath " ) & & settings . value ( " gamePath " ) . canConvert < QString > ( ) & & ! settings . value ( " gamePath " ) . toString ( ) . isEmpty ( ) ) {
profile . gamePath = settings . value ( " gamePath " ) . toString ( ) ;
} else {
2022-02-25 20:37:38 -05:00
profile . gamePath = getDefaultGamePath ( ) ;
2021-11-09 11:25:15 -05:00
}
2021-11-01 09:54:58 -04:00
2021-11-09 13:04:22 -05:00
if ( settings . contains ( " winePrefixPath " ) & & settings . value ( " winePrefixPath " ) . canConvert < QString > ( ) & & ! settings . value ( " winePrefixPath " ) . toString ( ) . isEmpty ( ) ) {
profile . winePrefixPath = settings . value ( " winePrefixPath " ) . toString ( ) ;
2021-11-09 11:25:15 -05:00
} else {
2022-02-25 20:37:38 -05:00
profile . winePrefixPath = getDefaultWinePrefixPath ( ) ;
2021-11-09 11:25:15 -05:00
}
2022-03-28 22:06:01 +08:00
if ( settings . contains ( " winePath " ) & & settings . value ( " winePath " ) . canConvert < QString > ( ) & & ! settings . value ( " winePath " ) . toString ( ) . isEmpty ( ) ) {
profile . winePath = settings . value ( " winePath " ) . toString ( ) ;
}
2022-02-25 20:32:08 -05:00
ProfileSettings defaultSettings ;
2021-11-09 21:13:21 -05:00
// login
2022-02-25 20:32:08 -05:00
profile . encryptArguments = settings . value ( " encryptArguments " , defaultSettings . encryptArguments ) . toBool ( ) ;
profile . isSapphire = settings . value ( " isSapphire " , defaultSettings . isSapphire ) . toBool ( ) ;
profile . lobbyURL = settings . value ( " lobbyURL " , defaultSettings . lobbyURL ) . toString ( ) ;
profile . rememberUsername = settings . value ( " rememberUsername " , defaultSettings . rememberUsername ) . toBool ( ) ;
profile . rememberPassword = settings . value ( " rememberPassword " , defaultSettings . rememberPassword ) . toBool ( ) ;
2022-03-16 09:59:27 -04:00
profile . license = ( GameLicense ) settings . value ( " license " , ( int ) defaultSettings . license ) . toInt ( ) ;
2021-11-09 12:32:18 -05:00
2022-02-25 20:32:08 -05:00
profile . useDX9 = settings . value ( " useDX9 " , defaultSettings . useDX9 ) . toBool ( ) ;
2022-04-09 17:28:24 -04:00
// wine
profile . wineType = ( WineType ) settings . value ( " wineType " , ( int ) defaultSettings . wineType ) . toInt ( ) ;
2022-02-25 20:32:08 -05:00
profile . useEsync = settings . value ( " useEsync " , defaultSettings . useEsync ) . toBool ( ) ;
2022-02-24 08:35:31 -05:00
if ( gamescopeAvailable )
2022-02-25 20:32:08 -05:00
profile . useGamescope = settings . value ( " useGamescope " , defaultSettings . useGamescope ) . toBool ( ) ;
2022-02-24 08:35:31 -05:00
if ( gamemodeAvailable )
2022-02-25 20:32:08 -05:00
profile . useGamemode = settings . value ( " useGamemode " , defaultSettings . useGamemode ) . toBool ( ) ;
2022-02-24 08:35:31 -05:00
2022-02-25 20:32:08 -05:00
profile . enableDXVKhud = settings . value ( " enableDXVKhud " , defaultSettings . enableDXVKhud ) . toBool ( ) ;
2022-04-09 17:28:24 -04:00
2022-02-25 20:32:08 -05:00
profile . enableWatchdog = settings . value ( " enableWatchdog " , defaultSettings . enableWatchdog ) . toBool ( ) ;
2021-11-03 06:31:02 -04:00
2022-02-23 21:18:53 -05:00
// gamescope
2022-02-25 20:32:08 -05:00
profile . gamescope . fullscreen = settings . value ( " gamescopeFullscreen " , defaultSettings . gamescope . fullscreen ) . toBool ( ) ;
profile . gamescope . borderless = settings . value ( " gamescopeBorderless " , defaultSettings . gamescope . borderless ) . toBool ( ) ;
profile . gamescope . width = settings . value ( " gamescopeWidth " , defaultSettings . gamescope . width ) . toInt ( ) ;
profile . gamescope . height = settings . value ( " gamescopeHeight " , defaultSettings . gamescope . height ) . toInt ( ) ;
profile . gamescope . refreshRate = settings . value ( " gamescopeRefreshRate " , defaultSettings . gamescope . refreshRate ) . toInt ( ) ;
2022-02-23 21:18:53 -05:00
2022-03-13 19:58:58 -04:00
profile . dalamud . enabled = settings . value ( " enableDalamud " , defaultSettings . dalamud . enabled ) . toBool ( ) ;
profile . dalamud . optOutOfMbCollection = settings . value ( " dalamudOptOut " , defaultSettings . dalamud . optOutOfMbCollection ) . toBool ( ) ;
2021-11-23 14:37:37 -05:00
2021-11-09 13:24:38 -05:00
profileSettings [ settings . value ( " index " ) . toInt ( ) ] = profile ;
2021-11-02 08:50:14 -04:00
2021-11-09 13:24:38 -05:00
settings . endGroup ( ) ;
2021-11-09 11:25:15 -05:00
}
2021-11-09 13:11:21 -05:00
readGameVersion ( ) ;
}
2021-11-23 15:34:23 -05:00
void LauncherCore : : readWineInfo ( ProfileSettings & profile ) {
2021-11-10 04:30:01 -05:00
# if defined(Q_OS_MAC)
2022-04-09 17:28:24 -04:00
switch ( profile . wineType ) {
case WineType : : System : // system wine
2021-11-10 04:30:01 -05:00
profile . winePath = " /usr/local/bin/wine64 " ;
break ;
2022-04-09 17:28:24 -04:00
case WineType : : Custom : // custom path
2021-11-10 04:30:01 -05:00
profile . winePath = profile . winePath ;
break ;
2022-04-09 17:28:24 -04:00
case WineType : : Builtin : // ffxiv built-in (for mac users)
2021-11-10 04:30:01 -05:00
profile . winePath = " /Applications/FINAL FANTASY XIV ONLINE.app/Contents/SharedSupport/finalfantasyxiv/FINAL FANTASY XIV ONLINE/wine " ;
break ;
}
# endif
# if defined(Q_OS_LINUX)
2022-04-09 17:28:24 -04:00
switch ( profile . wineType ) {
case WineType : : System : // system wine (should be in $PATH)
profile . winePath = " /usr/bin/wine " ;
2021-11-10 04:30:01 -05:00
break ;
2022-04-09 17:28:24 -04:00
case WineType : : Custom : // custom pth
2021-11-10 04:30:01 -05:00
profile . winePath = profile . winePath ;
break ;
}
# endif
2022-04-09 17:28:24 -04:00
# if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
auto wineProcess = new QProcess ( ) ;
wineProcess - > setProcessChannelMode ( QProcess : : MergedChannels ) ;
connect ( wineProcess , & QProcess : : readyRead , this , [ wineProcess , & profile ] {
profile . wineVersion = wineProcess - > readAllStandardOutput ( ) . trimmed ( ) ;
} ) ;
wineProcess - > start ( profile . winePath , { " --version " } ) ;
wineProcess - > waitForFinished ( ) ;
# endif
2021-11-10 04:30:01 -05:00
}
2021-11-23 15:34:23 -05:00
void LauncherCore : : readGameVersion ( ) {
2021-11-09 13:11:21 -05:00
for ( auto & profile : profileSettings ) {
profile . bootVersion = readVersion ( profile . gamePath + " /boot/ffxivboot.ver " ) ;
profile . gameVersion = readVersion ( profile . gamePath + " /game/ffxivgame.ver " ) ;
2022-03-17 01:03:08 -04:00
profile . installedMaxExpansion = 0 ;
2021-12-02 15:02:59 -05:00
for ( auto dir : QDir ( profile . gamePath + " /game/sqpack/ " ) . entryList ( QDir : : Filter : : Dirs ) ) {
if ( dir . contains ( " ex " ) & & dir . length ( ) = = 3 & & dir [ 2 ] . isDigit ( ) ) {
const int expacVersion = dir [ 2 ] . digitValue ( ) ;
profile . installedMaxExpansion = std : : max ( profile . installedMaxExpansion , expacVersion ) ;
}
}
readExpansionVersions ( profile , profile . installedMaxExpansion ) ;
2021-11-09 13:11:21 -05:00
}
2021-11-01 09:54:58 -04:00
}
2021-11-23 15:34:23 -05:00
LauncherCore : : LauncherCore ( ) : settings ( QSettings : : IniFormat , QSettings : : UserScope , QCoreApplication : : applicationName ( ) ) {
2021-11-01 09:54:58 -04:00
mgr = new QNetworkAccessManager ( ) ;
sapphireLauncher = new SapphireLauncher ( * this ) ;
squareLauncher = new SquareLauncher ( * this ) ;
squareBoot = new SquareBoot ( * this , * squareLauncher ) ;
2021-11-23 14:37:37 -05:00
assetUpdater = new AssetUpdater ( * this ) ;
2022-01-27 09:25:23 -05:00
# ifdef ENABLE_WATCHDOG
2021-12-06 21:15:31 -05:00
watchdog = new Watchdog ( * this ) ;
2022-01-27 09:25:23 -05:00
# endif
2021-11-01 09:54:58 -04:00
2021-11-09 11:25:15 -05:00
readInitialInformation ( ) ;
2021-12-02 14:17:33 -05:00
// check gate status before login
squareLauncher - > gateOpen ( ) ;
// TODO: we really should call this "heavy" signal
connect ( squareLauncher , & SquareLauncher : : gateStatusRecieved , this , & LauncherCore : : settingsChanged ) ;
2021-11-09 12:06:30 -05:00
}
2021-12-06 21:15:31 -05:00
LauncherCore : : ~ LauncherCore ( ) noexcept {
2022-01-27 09:25:23 -05:00
# ifdef ENABLE_WATCHDOG
2021-12-06 21:15:31 -05:00
delete watchdog ;
2022-01-27 09:25:23 -05:00
# endif
2021-12-06 21:15:31 -05:00
}
2021-11-23 15:34:23 -05:00
ProfileSettings LauncherCore : : getProfile ( int index ) const {
2021-11-09 12:06:30 -05:00
return profileSettings [ index ] ;
}
2021-11-23 15:34:23 -05:00
ProfileSettings & LauncherCore : : getProfile ( int index ) {
2021-11-09 12:06:30 -05:00
return profileSettings [ index ] ;
2021-11-09 11:03:44 -05:00
}
2021-11-23 15:34:23 -05:00
int LauncherCore : : getProfileIndex ( QString name ) {
2021-11-09 11:25:15 -05:00
for ( int i = 0 ; i < profileSettings . size ( ) ; i + + ) {
if ( profileSettings [ i ] . name = = name )
return i ;
2021-11-09 11:03:44 -05:00
}
2021-11-09 11:25:15 -05:00
return - 1 ;
2021-11-09 11:03:44 -05:00
}
2021-11-23 15:34:23 -05:00
QList < QString > LauncherCore : : profileList ( ) const {
2021-11-09 11:03:44 -05:00
QList < QString > list ;
for ( auto profile : profileSettings ) {
2021-11-09 11:25:15 -05:00
list . append ( profile . name ) ;
2021-11-09 11:03:44 -05:00
}
return list ;
2021-11-09 11:44:27 -05:00
}
2021-11-23 15:34:23 -05:00
int LauncherCore : : addProfile ( ) {
2021-11-09 11:44:27 -05:00
ProfileSettings newProfile ;
2021-11-09 13:24:38 -05:00
newProfile . uuid = QUuid : : createUuid ( ) ;
2021-11-09 11:44:27 -05:00
newProfile . name = " New Profile " ;
2022-02-25 20:37:38 -05:00
readWineInfo ( newProfile ) ;
newProfile . gamePath = getDefaultGamePath ( ) ;
newProfile . winePrefixPath = getDefaultWinePrefixPath ( ) ;
2021-11-09 11:44:27 -05:00
profileSettings . append ( newProfile ) ;
settingsChanged ( ) ;
return profileSettings . size ( ) - 1 ;
2021-11-09 12:16:14 -05:00
}
2021-11-23 15:34:23 -05:00
int LauncherCore : : deleteProfile ( QString name ) {
2021-11-09 13:44:37 -05:00
int index = 0 ;
for ( int i = 0 ; i < profileSettings . size ( ) ; i + + ) {
if ( profileSettings [ i ] . name = = name )
index = i ;
}
// remove group so it doesnt stay
settings . beginGroup ( profileSettings [ index ] . uuid . toString ( QUuid : : StringFormat : : WithoutBraces ) ) ;
settings . remove ( " " ) ;
settings . endGroup ( ) ;
profileSettings . removeAt ( index ) ;
return index - 1 ;
}
2021-11-23 15:34:23 -05:00
void LauncherCore : : saveSettings ( ) {
2021-11-09 13:24:38 -05:00
settings . setValue ( " defaultProfile " , defaultProfileIndex ) ;
2022-02-24 09:10:00 -05:00
settings . setValue ( " closeWhenLaunched " , appSettings . closeWhenLaunched ) ;
2022-04-10 16:58:29 -04:00
settings . setValue ( " showBanners " , appSettings . showBanners ) ;
settings . setValue ( " showNewsList " , appSettings . showNewsList ) ;
2021-11-09 13:24:38 -05:00
for ( int i = 0 ; i < profileSettings . size ( ) ; i + + ) {
const auto & profile = profileSettings [ i ] ;
2021-11-09 13:26:59 -05:00
settings . beginGroup ( profile . uuid . toString ( QUuid : : StringFormat : : WithoutBraces ) ) ;
2021-11-09 13:24:38 -05:00
settings . setValue ( " name " , profile . name ) ;
settings . setValue ( " index " , i ) ;
2021-11-09 12:16:14 -05:00
2021-11-09 13:04:22 -05:00
// game
2021-11-09 12:16:14 -05:00
settings . setValue ( " useDX9 " , profile . useDX9 ) ;
2021-11-09 12:46:27 -05:00
settings . setValue ( " gamePath " , profile . gamePath ) ;
2021-11-09 12:40:44 -05:00
2021-11-09 13:04:22 -05:00
// wine
2022-04-09 17:28:24 -04:00
settings . setValue ( " wineType " , ( int ) profile . wineType ) ;
2021-11-09 13:04:22 -05:00
settings . setValue ( " winePath " , profile . winePath ) ;
settings . setValue ( " winePrefixPath " , profile . winePrefixPath ) ;
2021-11-09 13:08:25 -05:00
settings . setValue ( " useEsync " , profile . useEsync ) ;
settings . setValue ( " useGamescope " , profile . useGamescope ) ;
settings . setValue ( " useGamemode " , profile . useGamemode ) ;
2022-02-23 21:18:53 -05:00
// gamescope
settings . setValue ( " gamescopeFullscreen " , profile . gamescope . fullscreen ) ;
settings . setValue ( " gamescopeBorderless " , profile . gamescope . borderless ) ;
settings . setValue ( " gamescopeWidth " , profile . gamescope . width ) ;
settings . setValue ( " gamescopeHeight " , profile . gamescope . height ) ;
settings . setValue ( " gamescopeRefreshRate " , profile . gamescope . refreshRate ) ;
2021-11-09 13:04:22 -05:00
// login
2021-11-09 21:13:21 -05:00
settings . setValue ( " encryptArguments " , profile . encryptArguments ) ;
2021-11-09 12:40:44 -05:00
settings . setValue ( " isSapphire " , profile . isSapphire ) ;
2021-11-09 12:50:37 -05:00
settings . setValue ( " lobbyURL " , profile . lobbyURL ) ;
2021-11-09 12:32:18 -05:00
settings . setValue ( " rememberUsername " , profile . rememberUsername ) ;
settings . setValue ( " rememberPassword " , profile . rememberPassword ) ;
2022-03-16 09:59:27 -04:00
settings . setValue ( " license " , ( int ) profile . license ) ;
2021-11-09 12:16:14 -05:00
2022-03-13 19:58:58 -04:00
settings . setValue ( " enableDalamud " , profile . dalamud . enabled ) ;
settings . setValue ( " dalamudOptOut " , profile . dalamud . optOutOfMbCollection ) ;
2021-12-06 21:15:31 -05:00
settings . setValue ( " enableWatchdog " , profile . enableWatchdog ) ;
2021-11-23 14:37:37 -05:00
2021-11-09 12:16:14 -05:00
settings . endGroup ( ) ;
}
2021-12-02 14:40:04 -05:00
}
void LauncherCore : : addUpdateButtons ( const ProfileSettings & settings , QMessageBox & messageBox ) {
auto launcherButton = messageBox . addButton ( " Launch Official Launcher " , QMessageBox : : NoRole ) ;
connect ( launcherButton , & QPushButton : : clicked , [ = ] {
2021-12-02 22:26:08 -05:00
launchExecutable ( settings , { settings . gamePath + " /boot/ffxivboot.exe " } ) ;
2021-12-02 14:40:04 -05:00
} ) ;
messageBox . addButton ( QMessageBox : : StandardButton : : Ok ) ;
2021-12-02 15:02:59 -05:00
}
void LauncherCore : : readExpansionVersions ( ProfileSettings & info , int max ) {
info . expansionVersions . clear ( ) ;
for ( int i = 0 ; i < max ; i + + )
info . expansionVersions . push_back ( readVersion ( QString ( " %1/game/sqpack/ex%2/ex%2.ver " ) . arg ( info . gamePath , QString : : number ( i + 1 ) ) ) ) ;
}
2022-02-24 08:35:31 -05:00
bool LauncherCore : : checkIfInPath ( const QString program ) {
// TODO: also check /usr/local/bin, /bin32 etc (basically read $PATH)
const QString directory = " /usr/bin " ;
QFileInfo fileInfo ( directory + " / " + program ) ;
return fileInfo . exists ( ) & & fileInfo . isFile ( ) ;
2022-02-25 20:37:38 -05:00
}
QString LauncherCore : : getDefaultWinePrefixPath ( ) {
# if defined(Q_OS_MACOS)
return QDir : : homePath ( ) + " /Library/Application Support/FINAL FANTASY XIV ONLINE/Bottles/published_Final_Fantasy " ;
# endif
# if defined(Q_OS_LINUX)
return QDir : : homePath ( ) + " /.wine " ;
# endif
2022-03-14 19:04:23 +00:00
return " " ;
2022-02-25 20:37:38 -05:00
}
QString LauncherCore : : getDefaultGamePath ( ) {
# if defined(Q_OS_WIN)
return " C: \\ Program Files (x86) \\ SquareEnix \\ FINAL FANTASY XIV - A Realm Reborn " ;
# endif
# if defined(Q_OS_MAC)
return QDir : : homePath ( ) + " /Library/Application Support/FINAL FANTASY XIV ONLINE/Bottles/published_Final_Fantasy/drive_c/Program Files (x86)/SquareEnix/FINAL FANTASY XIV - A Realm Reborn " ;
# endif
# if defined(Q_OS_LINUX)
return QDir : : homePath ( ) + " /.wine/drive_c/Program Files (x86)/SquareEnix/FINAL FANTASY XIV - A Realm Reborn " ;
# endif
}