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>
# include <keychain.h>
# include <QMessageBox>
2021-11-01 13:14:00 -04:00
# include <QMenuBar>
2021-11-01 09:54:58 -04:00
# include "xivlauncher.h"
# include "sapphirelauncher.h"
# include "squarelauncher.h"
# include "squareboot.h"
2021-11-01 13:14:00 -04:00
# include "settingswindow.h"
2021-11-01 09:54:58 -04:00
void LauncherWindow : : setSSL ( QNetworkRequest & request ) {
QSslConfiguration config ;
config . setProtocol ( QSsl : : AnyProtocol ) ;
config . setPeerVerifyMode ( QSslSocket : : VerifyNone ) ;
request . setSslConfiguration ( config ) ;
}
void LauncherWindow : : buildRequest ( QNetworkRequest & request ) {
setSSL ( request ) ;
request . setHeader ( QNetworkRequest : : UserAgentHeader ,
QString ( " SQEXAuthor/2.0.0(Windows 6.2; ja-jp; %1) " ) . arg ( QSysInfo : : bootUniqueId ( ) ) ) ;
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-02 08:36:30 -04:00
void LauncherWindow : : launchGame ( const LoginAuth auth ) {
QList < QString > arguments ;
// now for the actual game...
2021-11-09 11:03:44 -05:00
if ( currentProfile ( ) . useDX9 ) {
arguments . push_back ( currentProfile ( ) . gamePath + " \\ game \\ ffxiv.exe " ) ;
2021-11-02 08:49:06 -04:00
} else {
2021-11-09 11:03:44 -05:00
arguments . push_back ( currentProfile ( ) . gamePath + " \\ game \\ ffxiv_dx11.exe " ) ;
2021-11-02 08:49:06 -04:00
}
2021-11-02 08:36:30 -04:00
arguments . push_back ( " DEV.DataPathType=1 " ) ;
arguments . push_back ( " DEV.UseSqPack=1 " ) ;
arguments . push_back ( QString ( " DEV.MaxEntitledExpansionID=%1 " ) . arg ( auth . maxExpansion ) ) ;
arguments . push_back ( QString ( " DEV.TestSID=%1 " ) . arg ( auth . SID ) ) ;
arguments . push_back ( QString ( " SYS.Region=%1 " ) . arg ( auth . region ) ) ;
2021-11-09 11:03:44 -05:00
arguments . push_back ( QString ( " language=%1 " ) . arg ( currentProfile ( ) . language ) ) ;
arguments . push_back ( QString ( " ver=%1 " ) . arg ( currentProfile ( ) . gameVersion ) ) ;
2021-11-02 08:36:30 -04:00
if ( ! auth . lobbyhost . isEmpty ( ) ) {
arguments . push_back ( QString ( " DEV.GMServerHost=%1 " ) . arg ( auth . frontierHost ) ) ;
for ( int i = 1 ; i < 9 ; i + + )
arguments . push_back ( QString ( " DEV.LobbyHost0%1=%2 DEV.LobbyPort0%1=54994 " ) . arg ( QString : : number ( i ) , auth . lobbyhost ) ) ;
}
launchExecutable ( arguments ) ;
}
void LauncherWindow : : launchExecutable ( const QStringList args ) {
2021-11-01 09:54:58 -04:00
auto process = new QProcess ( this ) ;
process - > setProcessChannelMode ( QProcess : : ForwardedChannels ) ;
2021-11-01 14:35:32 -04:00
QList < QString > arguments ;
2021-11-02 20:04:53 -04:00
QStringList env = QProcess : : systemEnvironment ( ) ;
2021-11-01 09:54:58 -04:00
# if defined(Q_OS_LINUX)
2021-11-09 11:03:44 -05:00
if ( currentProfile ( ) . useGamescope ) {
2021-11-01 14:35:32 -04:00
arguments . push_back ( " gamescope " ) ;
arguments . push_back ( " -f " ) ;
arguments . push_back ( " -b " ) ;
}
2021-11-01 09:54:58 -04:00
2021-11-09 11:03:44 -05:00
if ( currentProfile ( ) . useGamemode )
2021-11-01 14:35:32 -04:00
arguments . push_back ( " gamemoderun " ) ;
2021-11-01 09:54:58 -04:00
2021-11-09 11:03:44 -05:00
if ( currentProfile ( ) . useEsync ) {
2021-11-01 14:35:32 -04:00
env < < " WINEESYNC=1 " ;
2021-11-01 09:54:58 -04: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)
2021-11-09 11:03:44 -05:00
env < < " WINEPREFIX= " + currentProfile ( ) . winePrefixPath ;
2021-11-01 14:35:32 -04:00
2021-11-09 11:03:44 -05:00
if ( currentProfile ( ) . enableDXVKhud )
2021-11-03 06:18:31 -04:00
env < < " DXVK_HUD=full " ;
2021-11-09 11:03:44 -05:00
arguments . push_back ( currentProfile ( ) . 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
2021-11-09 11:03:44 -05:00
process - > setWorkingDirectory ( currentProfile ( ) . gamePath + " /game/ " ) ;
2021-11-03 06:18:31 -04:00
process - > setEnvironment ( env ) ;
2021-11-01 14:35:32 -04:00
process - > start ( executable , arguments ) ;
2021-11-01 09:54:58 -04:00
}
QString LauncherWindow : : readVersion ( QString path ) {
QFile file ( path ) ;
file . open ( QFile : : OpenModeFlag : : ReadOnly ) ;
return file . readAll ( ) ;
}
void LauncherWindow : : readInitialInformation ( ) {
2021-11-09 12:25:54 -05:00
defaultProfileIndex = settings . value ( " defaultProfile " , 0 ) . toInt ( ) ;
2021-11-09 11:25:15 -05:00
auto profiles = settings . childGroups ( ) ;
// create the Default profile if it doesnt exist
if ( profiles . empty ( ) )
profiles . append ( " Default " ) ;
for ( const auto & profile_name : profiles ) {
ProfileSettings profile ;
profile . name = profile_name ;
settings . beginGroup ( profile_name ) ;
const int wineVersion = settings . value ( " wineVersion " , 0 ) . toInt ( ) ;
2021-11-03 06:18:31 -04:00
# if defined(Q_OS_MAC)
2021-11-09 11:25:15 -05:00
switch ( wineVersion ) {
2021-11-03 06:18:31 -04:00
case 0 : // system wine
2021-11-09 11:25:15 -05:00
profile . winePath = " /usr/local/bin/wine64 " ;
2021-11-03 06:18:31 -04:00
break ;
case 1 : // custom path
2021-11-09 11:25:15 -05:00
profile . winePath = settings . value ( " winePath " ) . toString ( ) ;
2021-11-03 06:18:31 -04:00
break ;
case 2 : // ffxiv built-in (for mac users)
2021-11-09 11:25:15 -05:00
profile . winePath = " /Applications/FINAL FANTASY XIV ONLINE.app/Contents/SharedSupport/finalfantasyxiv/FINAL FANTASY XIV ONLINE/wine " ;
2021-11-03 06:18:31 -04:00
break ;
}
# endif
# if defined(Q_OS_LINUX)
2021-11-09 11:25:15 -05:00
switch ( wineVersion ) {
case 0 : // system wine (should be in $PATH)
profile . winePath = " wine " ;
break ;
case 1 : // custom pth
profile . winePath = settings . value ( " winePath " ) . toString ( ) ;
break ;
}
2021-11-03 06:18:31 -04:00
# endif
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 {
2021-11-01 09:54:58 -04:00
# if defined(Q_OS_WIN)
2021-11-09 11:25:15 -05:00
profile . gamePath = " C: \\ Program Files (x86) \\ SquareEnix \\ FINAL FANTASY XIV - A Realm Reborn " ;
2021-11-01 09:54:58 -04:00
# endif
# if defined(Q_OS_MACOS)
2021-11-09 11:25:15 -05:00
profile . gamePath = 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 " ;
2021-11-01 09:54:58 -04:00
# endif
# if defined(Q_OS_LINUX)
2021-11-09 11:25:15 -05:00
profile . gamePath = QDir : : homePath ( ) + " /.wine/drive_c/Program Files (x86)/SquareEnix/FINAL FANTASY XIV - A Realm Reborn " ;
2021-11-01 09:54:58 -04:00
# endif
2021-11-09 11:25:15 -05:00
}
2021-11-01 09:54:58 -04:00
2021-11-09 11:25:15 -05:00
if ( settings . contains ( " winePrefix " ) & & settings . value ( " winePrefix " ) . canConvert < QString > ( ) & & ! settings . value ( " winePrefix " ) . toString ( ) . isEmpty ( ) ) {
profile . winePrefixPath = settings . value ( " winePrefix " ) . toString ( ) ;
} else {
2021-11-03 06:31:02 -04:00
# if defined(Q_OS_MACOS)
2021-11-09 11:25:15 -05:00
profile . winePrefixPath = QDir : : homePath ( ) + " /Library/Application Support/FINAL FANTASY XIV ONLINE/Bottles/published_Final_Fantasy " ;
2021-11-03 06:31:02 -04:00
# endif
# if defined(Q_OS_LINUX)
2021-11-09 11:25:15 -05:00
profile . winePrefixPath = QDir : : homePath ( ) + " /.wine " ;
2021-11-03 06:31:02 -04:00
# endif
2021-11-09 11:25:15 -05:00
}
profile . bootVersion = readVersion ( profile . gamePath + " /boot/ffxivboot.ver " ) ;
profile . gameVersion = readVersion ( profile . gamePath + " /game/ffxivgame.ver " ) ;
2021-11-09 12:32:18 -05:00
profile . rememberUsername = settings . value ( " rememberUsername " , false ) . toBool ( ) ;
profile . rememberPassword = settings . value ( " rememberPassword " , false ) . toBool ( ) ;
2021-11-09 12:16:14 -05:00
profile . useDX9 = settings . value ( " useDX9 " , false ) . toBool ( ) ;
2021-11-09 11:25:15 -05:00
profile . useEsync = settings . value ( " useEsync " , false ) . toBool ( ) ;
profile . useGamemode = settings . value ( " useGamemode " , false ) . toBool ( ) ;
profile . useGamescope = settings . value ( " useGamescope " , false ) . toBool ( ) ;
profile . enableDXVKhud = settings . value ( " enableDXVKhud " , false ) . toBool ( ) ;
2021-11-03 06:31:02 -04:00
2021-11-09 11:25:15 -05:00
settings . endGroup ( ) ;
2021-11-02 08:50:14 -04:00
2021-11-09 11:25:15 -05:00
profileSettings . append ( profile ) ;
}
2021-11-01 09:54:58 -04:00
}
LauncherWindow : : LauncherWindow ( QWidget * parent ) :
QMainWindow ( parent ) {
mgr = new QNetworkAccessManager ( ) ;
sapphireLauncher = new SapphireLauncher ( * this ) ;
squareLauncher = new SquareLauncher ( * this ) ;
squareBoot = new SquareBoot ( * this , * squareLauncher ) ;
2021-11-09 11:25:15 -05:00
readInitialInformation ( ) ;
2021-11-09 11:03:44 -05:00
2021-11-01 13:14:00 -04:00
QMenu * fileMenu = menuBar ( ) - > addMenu ( " File " ) ;
2021-11-01 13:22:27 -04:00
// sorry linux users, for some reason my global menu does not like qt6 apps right now
# if defined(Q_OS_LINUX)
menuBar ( ) - > setNativeMenuBar ( false ) ;
# endif
2021-11-01 13:14:00 -04:00
QAction * settingsAction = fileMenu - > addAction ( " Settings... " ) ;
connect ( settingsAction , & QAction : : triggered , [ = ] {
auto window = new SettingsWindow ( * this ) ;
2021-11-09 11:44:27 -05:00
connect ( this , & LauncherWindow : : settingsChanged , window , & SettingsWindow : : reloadControls ) ;
2021-11-01 13:14:00 -04:00
window - > show ( ) ;
} ) ;
2021-11-02 08:36:30 -04:00
QMenu * toolsMenu = menuBar ( ) - > addMenu ( " Tools " ) ;
QAction * launchOfficial = toolsMenu - > addAction ( " Launch Official Client... " ) ;
connect ( launchOfficial , & QAction : : triggered , [ = ] {
2021-11-09 11:03:44 -05:00
launchExecutable ( { currentProfile ( ) . gamePath + " /boot/ffxivboot64.exe " } ) ;
2021-11-02 08:36:30 -04:00
} ) ;
2021-11-02 08:38:04 -04:00
QAction * launchSysInfo = toolsMenu - > addAction ( " Launch System Info... " ) ;
connect ( launchSysInfo , & QAction : : triggered , [ = ] {
2021-11-09 11:03:44 -05:00
launchExecutable ( { currentProfile ( ) . gamePath + " /boot/ffxivsysinfo64.exe " } ) ;
2021-11-02 08:38:04 -04:00
} ) ;
2021-11-02 08:39:52 -04:00
QAction * launchCfgBackup = toolsMenu - > addAction ( " Launch Config Backup... " ) ;
connect ( launchCfgBackup , & QAction : : triggered , [ = ] {
2021-11-09 11:03:44 -05:00
launchExecutable ( { currentProfile ( ) . gamePath + " /boot/ffxivconfig64.exe " } ) ;
2021-11-02 08:39:52 -04:00
} ) ;
2021-11-02 14:59:05 -04:00
# if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
QMenu * wineMenu = toolsMenu - > addMenu ( " Wine " ) ;
QAction * wineCfg = wineMenu - > addAction ( " winecfg " ) ;
connect ( wineCfg , & QAction : : triggered , [ = ] {
launchExecutable ( { " winecfg.exe " } ) ;
} ) ;
QAction * controlPanel = wineMenu - > addAction ( " Control Panel " ) ;
connect ( controlPanel , & QAction : : triggered , [ = ] {
launchExecutable ( { " control.exe " } ) ;
} ) ;
# endif
2021-11-01 09:54:58 -04:00
auto layout = new QFormLayout ( ) ;
2021-11-09 12:25:54 -05:00
profileSelect = new QComboBox ( ) ;
2021-11-09 10:37:48 -05:00
layout - > addRow ( " Profile " , profileSelect ) ;
2021-11-09 12:25:54 -05:00
usernameEdit = new QLineEdit ( ) ;
2021-11-01 09:54:58 -04:00
layout - > addRow ( " Username " , usernameEdit ) ;
2021-11-09 12:25:54 -05:00
rememberUsernameBox = new QCheckBox ( ) ;
2021-11-09 12:32:18 -05:00
connect ( rememberUsernameBox , & QCheckBox : : stateChanged , [ = ] ( int ) {
currentProfile ( ) . rememberUsername = rememberUsernameBox - > isChecked ( ) ;
saveSettings ( ) ;
} ) ;
2021-11-01 09:54:58 -04:00
layout - > addRow ( " Remember Username? " , rememberUsernameBox ) ;
2021-11-09 12:25:54 -05:00
passwordEdit = new QLineEdit ( ) ;
2021-11-01 09:54:58 -04:00
passwordEdit - > setEchoMode ( QLineEdit : : EchoMode : : Password ) ;
layout - > addRow ( " Password " , passwordEdit ) ;
2021-11-09 12:25:54 -05:00
rememberPasswordBox = new QCheckBox ( ) ;
2021-11-09 12:32:18 -05:00
connect ( rememberPasswordBox , & QCheckBox : : stateChanged , [ = ] ( int ) {
currentProfile ( ) . rememberPassword = rememberPasswordBox - > isChecked ( ) ;
saveSettings ( ) ;
} ) ;
2021-11-01 09:54:58 -04:00
layout - > addRow ( " Remember Password? " , rememberPasswordBox ) ;
2021-11-09 12:25:54 -05:00
otpEdit = new QLineEdit ( ) ;
2021-11-01 09:54:58 -04:00
layout - > addRow ( " One-Time Password " , otpEdit ) ;
auto loginButton = new QPushButton ( " Login " ) ;
layout - > addRow ( loginButton ) ;
2021-11-09 12:25:54 -05:00
registerButton = new QPushButton ( " Register " ) ;
2021-11-01 09:54:58 -04:00
layout - > addRow ( registerButton ) ;
auto emptyWidget = new QWidget ( ) ;
emptyWidget - > setLayout ( layout ) ;
setCentralWidget ( emptyWidget ) ;
connect ( loginButton , & QPushButton : : released , [ = ] {
auto info = LoginInformation { usernameEdit - > text ( ) , passwordEdit - > text ( ) , otpEdit - > text ( ) } ;
2021-11-09 11:25:15 -05:00
if ( currentProfile ( ) . rememberUsername ) {
2021-11-01 09:54:58 -04:00
auto job = new QKeychain : : WritePasswordJob ( " LauncherWindow " ) ;
job - > setTextData ( usernameEdit - > text ( ) ) ;
2021-11-09 12:33:20 -05:00
job - > setKey ( currentProfile ( ) . name + " username " ) ;
2021-11-01 09:54:58 -04:00
job - > start ( ) ;
}
2021-11-09 11:25:15 -05:00
if ( currentProfile ( ) . rememberPassword ) {
2021-11-01 09:54:58 -04:00
auto job = new QKeychain : : WritePasswordJob ( " LauncherWindow " ) ;
job - > setTextData ( passwordEdit - > text ( ) ) ;
2021-11-09 12:33:20 -05:00
job - > setKey ( currentProfile ( ) . name + " password " ) ;
2021-11-01 09:54:58 -04:00
job - > start ( ) ;
}
2021-11-09 11:25:15 -05:00
if ( currentProfile ( ) . isSapphire ) {
sapphireLauncher - > login ( currentProfile ( ) . lobbyURL , info ) ;
} else {
2021-11-01 09:54:58 -04:00
squareBoot - > bootCheck ( info ) ;
2021-11-09 11:25:15 -05:00
}
2021-11-01 09:54:58 -04:00
} ) ;
2021-11-09 11:25:15 -05:00
connect ( registerButton , & QPushButton : : released , [ = ] {
if ( currentProfile ( ) . isSapphire ) {
2021-11-01 09:54:58 -04:00
auto info = LoginInformation { usernameEdit - > text ( ) , passwordEdit - > text ( ) , otpEdit - > text ( ) } ;
2021-11-09 11:25:15 -05:00
sapphireLauncher - > registerAccount ( currentProfile ( ) . lobbyURL , info ) ;
2021-11-01 09:54:58 -04:00
}
2021-11-09 11:25:15 -05:00
} ) ;
2021-11-09 12:25:54 -05:00
reloadControls ( ) ;
2021-11-01 09:54:58 -04:00
}
LauncherWindow : : ~ LauncherWindow ( ) = default ;
2021-11-09 11:03:44 -05:00
ProfileSettings LauncherWindow : : currentProfile ( ) const {
2021-11-09 12:25:54 -05:00
return getProfile ( profileSelect - > currentIndex ( ) ) ;
2021-11-09 11:03:44 -05:00
}
ProfileSettings & LauncherWindow : : currentProfile ( ) {
2021-11-09 12:25:54 -05:00
return getProfile ( profileSelect - > currentIndex ( ) ) ;
2021-11-09 12:06:30 -05:00
}
ProfileSettings LauncherWindow : : getProfile ( int index ) const {
return profileSettings [ index ] ;
}
ProfileSettings & LauncherWindow : : getProfile ( int index ) {
return profileSettings [ index ] ;
2021-11-09 11:03:44 -05:00
}
2021-11-09 11:25:15 -05:00
int LauncherWindow : : getProfileIndex ( QString name ) {
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
}
QList < QString > LauncherWindow : : profileList ( ) const {
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
}
int LauncherWindow : : addProfile ( ) {
ProfileSettings newProfile ;
newProfile . name = " New Profile " ;
profileSettings . append ( newProfile ) ;
settingsChanged ( ) ;
return profileSettings . size ( ) - 1 ;
2021-11-09 12:16:14 -05:00
}
void LauncherWindow : : saveSettings ( ) {
for ( auto profile : profileSettings ) {
settings . beginGroup ( profile . name ) ;
settings . setValue ( " useDX9 " , profile . useDX9 ) ;
2021-11-09 12:32:18 -05:00
settings . setValue ( " rememberUsername " , profile . rememberUsername ) ;
settings . setValue ( " rememberPassword " , profile . rememberPassword ) ;
2021-11-09 12:16:14 -05:00
settings . endGroup ( ) ;
}
2021-11-09 12:25:54 -05:00
}
void LauncherWindow : : reloadControls ( ) {
profileSelect - > clear ( ) ;
for ( const auto & profile : profileList ( ) ) {
profileSelect - > addItem ( profile ) ;
}
if ( profileSelect - > currentIndex ( ) = = - 1 ) {
profileSelect - > setCurrentIndex ( defaultProfileIndex ) ;
}
rememberUsernameBox - > setChecked ( currentProfile ( ) . rememberUsername ) ;
if ( currentProfile ( ) . rememberUsername ) {
auto job = new QKeychain : : ReadPasswordJob ( " LauncherWindow " ) ;
2021-11-09 12:33:20 -05:00
job - > setKey ( currentProfile ( ) . name + " -username " ) ;
2021-11-09 12:25:54 -05:00
job - > start ( ) ;
connect ( job , & QKeychain : : ReadPasswordJob : : finished , [ = ] ( QKeychain : : Job * j ) {
usernameEdit - > setText ( job - > textData ( ) ) ;
} ) ;
}
rememberPasswordBox - > setChecked ( currentProfile ( ) . rememberPassword ) ;
if ( currentProfile ( ) . rememberPassword ) {
auto job = new QKeychain : : ReadPasswordJob ( " LauncherWindow " ) ;
2021-11-09 12:33:20 -05:00
job - > setKey ( currentProfile ( ) . name + " -password " ) ;
2021-11-09 12:25:54 -05:00
job - > start ( ) ;
connect ( job , & QKeychain : : ReadPasswordJob : : finished , [ = ] ( QKeychain : : Job * j ) {
passwordEdit - > setText ( job - > textData ( ) ) ;
} ) ;
}
registerButton - > setEnabled ( currentProfile ( ) . isSapphire ) ;
otpEdit - > setEnabled ( ! currentProfile ( ) . isSapphire ) ;
2021-11-09 11:03:44 -05:00
}