2021-11-01 09:54:58 -04:00
# include "squarelauncher.h"
# include <QFile>
# include <QUrlQuery>
# include <QNetworkReply>
# include <QRegularExpressionMatch>
# include <QMessageBox>
2021-12-02 14:17:33 -05:00
# include <QJsonDocument>
# include <QJsonObject>
2022-03-16 14:40:55 -04:00
# include <QPushButton>
# include <QDesktopServices>
2021-11-01 09:54:58 -04:00
2021-11-23 15:34:23 -05:00
# include "launchercore.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
2022-07-21 21:38:26 -04:00
SquareLauncher : : SquareLauncher ( LauncherCore & window ) : window ( window ) , QObject ( & window ) {
2021-11-01 09:54:58 -04:00
}
QString getFileHash ( QString file ) {
auto f = QFile ( file ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) )
return " " ;
QCryptographicHash hash ( QCryptographicHash : : Sha1 ) ;
hash . addData ( & f ) ;
return QString ( " %1/%2 " ) . arg ( QString : : number ( f . size ( ) ) , hash . result ( ) . toHex ( ) ) ;
}
void SquareLauncher : : getStored ( const LoginInformation & info ) {
QUrlQuery query ;
2022-03-16 14:33:04 -04:00
// en is always used to the top url
2021-11-01 09:54:58 -04:00
query . addQueryItem ( " lng " , " en " ) ;
2022-03-16 14:33:04 -04:00
// for some reason, we always use region 3. the actual region is acquired later
2021-11-01 09:54:58 -04:00
query . addQueryItem ( " rgn " , " 3 " ) ;
2022-05-09 16:02:07 -04:00
query . addQueryItem ( " isft " , info . settings - > isFreeTrial ? " 1 " : " 0 " ) ;
2021-11-01 09:54:58 -04:00
query . addQueryItem ( " cssmode " , " 1 " ) ;
query . addQueryItem ( " isnew " , " 1 " ) ;
2022-03-16 14:33:04 -04:00
query . addQueryItem ( " launchver " , " 3 " ) ;
if ( info . settings - > license = = GameLicense : : WindowsSteam ) {
query . addQueryItem ( " issteam " , " 1 " ) ;
// TODO: get steam ticket information from steam api
query . addQueryItem ( " session_ticket " , " 1 " ) ;
query . addQueryItem ( " ticket_size " , " 1 " ) ;
}
2021-11-01 09:54:58 -04:00
QUrl url ( " https://ffxiv-login.square-enix.com/oauth/ffxivarr/login/top " ) ;
url . setQuery ( query ) ;
auto request = QNetworkRequest ( url ) ;
2022-03-16 15:03:35 -04:00
window . buildRequest ( * info . settings , request ) ;
2021-11-01 09:54:58 -04:00
QNetworkReply * reply = window . mgr - > get ( request ) ;
2022-06-08 13:55:15 -04:00
connect ( reply , & QNetworkReply : : finished , [ = , & info ] {
2021-11-01 09:54:58 -04:00
auto str = QString ( reply - > readAll ( ) ) ;
2022-03-16 14:33:04 -04:00
// fetches Steam username
if ( info . settings - > license = = GameLicense : : WindowsSteam ) {
QRegularExpression re ( R " lit(<input name= " " sqexid " " type= " " hidden " " value= " " (?<sqexid>.*) " " \ / > ) lit " ) ;
QRegularExpressionMatch match = re . match ( str ) ;
if ( match . hasMatch ( ) ) {
username = match . captured ( 1 ) ;
} else {
auto messageBox = new QMessageBox ( QMessageBox : : Icon : : Critical , " Failed to Login " , " Could not get Steam username, have you attached your account? " ) ;
messageBox - > show ( ) ;
}
} else {
username = info . username ;
}
2021-11-01 09:54:58 -04:00
QRegularExpression re ( R " lit( \t < \ s*input .* name= " _STORED_ " value= " ( ? < stored > . * ) " >)lit " ) ;
QRegularExpressionMatch match = re . match ( str ) ;
if ( match . hasMatch ( ) ) {
stored = match . captured ( 1 ) ;
login ( info , url ) ;
} else {
auto messageBox = new QMessageBox ( QMessageBox : : Icon : : Critical , " Failed to Login " , " Failed to contact SE servers. They may be in maintenance. " ) ;
messageBox - > show ( ) ;
}
} ) ;
}
void SquareLauncher : : login ( const LoginInformation & info , const QUrl referer ) {
QUrlQuery postData ;
postData . addQueryItem ( " _STORED_ " , stored ) ;
postData . addQueryItem ( " sqexid " , info . username ) ;
postData . addQueryItem ( " password " , info . password ) ;
postData . addQueryItem ( " otppw " , info . oneTimePassword ) ;
QNetworkRequest request ( QUrl ( " https://ffxiv-login.square-enix.com/oauth/ffxivarr/login/login.send " ) ) ;
2022-03-16 15:03:35 -04:00
window . buildRequest ( * info . settings , request ) ;
2021-11-01 09:54:58 -04:00
request . setHeader ( QNetworkRequest : : ContentTypeHeader , " application/x-www-form-urlencoded " ) ;
request . setRawHeader ( " Referer " , referer . toEncoded ( ) ) ;
request . setRawHeader ( " Cache-Control " , " no-cache " ) ;
auto reply = window . mgr - > post ( request , postData . toString ( QUrl : : FullyEncoded ) . toUtf8 ( ) ) ;
2022-06-08 13:55:15 -04:00
connect ( reply , & QNetworkReply : : finished , [ = , & info ] {
2021-11-01 09:54:58 -04:00
auto str = QString ( reply - > readAll ( ) ) ;
QRegularExpression re ( R " lit(window.external.user \ ( " login = auth , ok , ( ? < launchParams > . * ) \ ) ; ) lit " );
QRegularExpressionMatch match = re . match ( str ) ;
if ( match . hasMatch ( ) ) {
const auto parts = match . captured ( 1 ) . split ( ' , ' ) ;
const bool terms = parts [ 3 ] = = " 1 " ;
const bool playable = parts [ 9 ] = = " 1 " ;
2022-03-16 14:33:04 -04:00
if ( ! playable ) {
auto messageBox = new QMessageBox ( QMessageBox : : Icon : : Critical , " Failed to Login " , " Your game is unplayable. Please check that you have the right license selected, and a subscription to play. " ) ;
2022-03-16 14:40:55 -04:00
auto launcherButton = messageBox - > addButton ( " Open Mog Station " , QMessageBox : : HelpRole ) ;
connect ( launcherButton , & QPushButton : : clicked , [ = ] {
QDesktopServices : : openUrl ( QUrl ( " https://sqex.to/Msp " ) ) ;
} ) ;
messageBox - > addButton ( QMessageBox : : StandardButton : : Ok ) ;
2021-12-02 14:40:04 -05:00
2021-11-01 09:54:58 -04:00
messageBox - > show ( ) ;
2022-03-16 14:33:04 -04:00
return ;
2021-11-01 09:54:58 -04:00
}
2022-03-16 14:33:04 -04:00
if ( ! terms ) {
auto messageBox = new QMessageBox ( QMessageBox : : Icon : : Critical , " Failed to Login " , " Your game is unplayable. You need to accept the terms of service from the official launcher. " ) ;
window . addUpdateButtons ( * info . settings , * messageBox ) ;
messageBox - > show ( ) ;
return ;
}
SID = parts [ 1 ] ;
auth . region = parts [ 5 ] . toInt ( ) ;
auth . maxExpansion = parts [ 13 ] . toInt ( ) ;
registerSession ( info ) ;
2021-11-01 09:54:58 -04:00
} else {
2022-03-16 14:51:16 -04:00
QRegularExpression re ( R " lit(window.external.user \ ( " login = auth , ng , err , ( ? < launchParams > . * ) \ ) ; ) lit " );
QRegularExpressionMatch match = re . match ( str ) ;
const auto parts = match . captured ( 1 ) . split ( ' , ' ) ;
// there's a stray quote at the end of the error string, so let's remove that
QString errorStr = match . captured ( 1 ) . chopped ( 1 ) ;
auto messageBox = new QMessageBox ( QMessageBox : : Icon : : Critical , " Failed to Login " , errorStr ) ;
2021-11-01 09:54:58 -04:00
messageBox - > show ( ) ;
}
} ) ;
}
void SquareLauncher : : registerSession ( const LoginInformation & info ) {
QUrl url ;
url . setScheme ( " https " ) ;
url . setHost ( " patch-gamever.ffxiv.com " ) ;
2022-08-09 23:18:18 -04:00
url . setPath ( QString ( " /http/win32/ffxivneo_release_game/%1/%2 " ) . arg ( info . settings - > repositories . repositories [ 0 ] . version , SID ) ) ;
2021-11-01 09:54:58 -04:00
auto request = QNetworkRequest ( url ) ;
window . setSSL ( request ) ;
request . setRawHeader ( " X-Hash-Check " , " enabled " ) ;
request . setRawHeader ( " User-Agent " , " FFXIV PATCH CLIENT " ) ;
request . setHeader ( QNetworkRequest : : ContentTypeHeader , " application/x-www-form-urlencoded " ) ;
2022-08-09 23:18:18 -04:00
QString report = QString ( " %1=%2 " ) . arg ( info . settings - > bootVersion , getBootHash ( info ) ) ;
2021-11-01 09:54:58 -04:00
2022-07-20 17:58:21 -04:00
for ( int i = 1 ; i < auth . maxExpansion + 1 ; i + + ) {
2022-08-09 23:18:18 -04:00
if ( i < = info . settings - > repositories . repositories_count ) {
2022-07-20 17:58:21 -04:00
report + = QString ( " \n ex%1 \t %2 " )
2022-08-09 23:18:18 -04:00
. arg ( QString : : number ( i ) , info . settings - > repositories . repositories [ i ] . version ) ;
2022-07-20 17:58:21 -04:00
} else {
report + = QString ( " \n ex%1 \t 2012.01.01.0000.0000 " ) . arg ( QString : : number ( i ) ) ;
}
}
2021-11-01 09:54:58 -04:00
auto reply = window . mgr - > post ( request , report . toUtf8 ( ) ) ;
2022-07-20 17:58:35 -04:00
connect ( reply , & QNetworkReply : : finished , [ = , & info ] {
2021-11-01 09:54:58 -04:00
if ( reply - > rawHeaderList ( ) . contains ( " X-Patch-Unique-Id " ) ) {
2022-07-20 18:00:42 -04:00
QString body = reply - > readAll ( ) ;
2022-08-09 22:44:10 -04:00
patcher = new Patcher ( info . settings - > gamePath + " /game " , info . settings - > gameData ) ;
2022-07-20 18:00:42 -04:00
connect ( patcher , & Patcher : : done , [ = , & info ] {
window . readGameVersion ( ) ;
auth . SID = reply - > rawHeader ( " X-Patch-Unique-Id " ) ;
2021-11-01 09:54:58 -04:00
2022-01-27 09:25:23 -05:00
# ifdef ENABLE_WATCHDOG
2022-07-20 18:00:42 -04:00
if ( info . settings - > enableWatchdog ) {
window . watchdog - > launchGame ( * info . settings , auth ) ;
} else {
window . launchGame ( * info . settings , auth ) ;
}
2022-01-27 09:25:23 -05:00
# else
2022-07-20 18:00:42 -04:00
window . launchGame ( * info . settings , auth ) ;
2022-01-27 09:25:23 -05:00
# endif
2022-07-20 18:00:42 -04:00
} ) ;
patcher - > processPatchList ( * window . mgr , body ) ;
2021-11-01 09:54:58 -04:00
} else {
auto messageBox = new QMessageBox ( QMessageBox : : Icon : : Critical , " Failed to Login " , " Failed the anti-tamper check. Please restore your game to the original state or update the game. " ) ;
2021-12-02 14:40:04 -05:00
window . addUpdateButtons ( * info . settings , * messageBox ) ;
2021-11-01 09:54:58 -04:00
messageBox - > show ( ) ;
}
} ) ;
}
2021-11-23 15:34:23 -05:00
QString SquareLauncher : : getBootHash ( const LoginInformation & info ) {
2021-11-01 09:54:58 -04:00
const QList < QString > fileList =
{
" ffxivboot.exe " ,
" ffxivboot64.exe " ,
" ffxivlauncher.exe " ,
" ffxivlauncher64.exe " ,
" ffxivupdater.exe " ,
" ffxivupdater64.exe "
} ;
QString result ;
for ( int i = 0 ; i < fileList . count ( ) ; i + + ) {
2021-11-23 15:34:23 -05:00
result + = fileList [ i ] + " / " + getFileHash ( info . settings - > gamePath + " /boot/ " + fileList [ i ] ) ;
2021-11-01 09:54:58 -04:00
if ( i ! = fileList . length ( ) - 1 )
result + = " , " ;
}
return result ;
2022-05-09 15:53:17 -04:00
}