2019-04-09 22:13:54 +02:00
# include " game_compatibility.h "
2017-12-04 21:59:28 +01:00
2018-03-17 00:13:40 +01:00
# include <QLabel>
2017-12-04 21:59:28 +01:00
# include <QMessageBox>
2020-02-04 13:40:02 +01:00
LOG_CHANNEL ( compat_log , " Compat " ) ;
2020-02-01 05:15:50 +01:00
2017-12-04 21:59:28 +01:00
constexpr auto qstr = QString : : fromStdString ;
inline std : : string sstr ( const QString & _in ) { return _in . toStdString ( ) ; }
game_compatibility : : game_compatibility ( std : : shared_ptr < gui_settings > settings ) : m_xgui_settings ( settings )
{
m_filepath = m_xgui_settings - > GetSettingsDir ( ) + " /compat_database.dat " ;
m_url = " https://rpcs3.net/compatibility?api=v1&export " ;
m_network_request = QNetworkRequest ( QUrl ( m_url ) ) ;
RequestCompatibility ( ) ;
}
2018-06-16 16:34:31 +02:00
bool game_compatibility : : ReadJSON ( const QJsonObject & json_data , bool after_download )
2017-12-04 21:59:28 +01:00
{
2018-06-16 16:34:31 +02:00
int return_code = json_data [ " return_code " ] . toInt ( ) ;
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
if ( return_code < 0 )
{
if ( after_download )
2017-12-04 21:59:28 +01:00
{
2018-06-16 16:34:31 +02:00
std : : string error_message ;
switch ( return_code )
2017-12-04 21:59:28 +01:00
{
2018-06-16 16:34:31 +02:00
case - 1 :
error_message = " Server Error - Internal Error " ;
break ;
case - 2 :
error_message = " Server Error - Maintenance Mode " ;
break ;
default :
error_message = " Server Error - Unknown Error " ;
break ;
2017-12-04 21:59:28 +01:00
}
2020-02-04 13:40:02 +01:00
compat_log . error ( " %s: return code %d " , error_message , return_code ) ;
2018-06-16 16:34:31 +02:00
Q_EMIT DownloadError ( qstr ( error_message ) + " " + QString : : number ( return_code ) ) ;
2017-12-04 21:59:28 +01:00
}
2018-06-16 16:34:31 +02:00
else
2017-12-04 21:59:28 +01:00
{
2020-02-04 13:40:02 +01:00
compat_log . error ( " Database Error - Invalid: return code %d " , return_code ) ;
2017-12-04 21:59:28 +01:00
}
2018-06-16 16:34:31 +02:00
return false ;
}
if ( ! json_data [ " results " ] . isObject ( ) )
{
2020-02-04 13:40:02 +01:00
compat_log . error ( " Database Error - No Results found " ) ;
2018-06-16 16:34:31 +02:00
return false ;
}
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
m_compat_database . clear ( ) ;
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
QJsonObject json_results = json_data [ " results " ] . toObject ( ) ;
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
// Retrieve status data for every valid entry
for ( const auto & key : json_results . keys ( ) )
{
if ( ! json_results [ key ] . isObject ( ) )
2017-12-04 21:59:28 +01:00
{
2020-02-04 13:40:02 +01:00
compat_log . error ( " Database Error - Unusable object %s " , sstr ( key ) ) ;
2018-06-16 16:34:31 +02:00
continue ;
}
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
QJsonObject json_result = json_results [ key ] . toObject ( ) ;
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
// Retrieve compatibility information from json
compat_status status = Status_Data . at ( json_result . value ( " status " ) . toString ( " NoResult " ) ) ;
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
// Add date if possible
status . date = json_result . value ( " date " ) . toString ( ) ;
2017-12-04 21:59:28 +01:00
2019-04-09 22:13:54 +02:00
// Add version if possible
status . version = json_result . value ( " update " ) . toString ( ) ;
2018-06-16 16:34:31 +02:00
// Add status to map
m_compat_database . emplace ( std : : pair < std : : string , compat_status > ( sstr ( key ) , status ) ) ;
}
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
return true ;
}
2017-12-04 21:59:28 +01:00
2018-06-16 16:34:31 +02:00
void game_compatibility : : RequestCompatibility ( bool online )
{
2017-12-04 21:59:28 +01:00
if ( ! online )
{
// Retrieve database from file
QFile file ( m_filepath ) ;
if ( ! file . exists ( ) )
{
2020-02-04 13:40:02 +01:00
compat_log . notice ( " Database file not found: %s " , sstr ( m_filepath ) ) ;
2017-12-04 21:59:28 +01:00
return ;
}
if ( ! file . open ( QIODevice : : ReadOnly ) )
{
2020-02-04 13:40:02 +01:00
compat_log . error ( " Database Error - Could not read database from file: %s " , sstr ( m_filepath ) ) ;
2017-12-04 21:59:28 +01:00
return ;
}
QByteArray data = file . readAll ( ) ;
file . close ( ) ;
2020-02-04 13:40:02 +01:00
compat_log . notice ( " Finished reading database from file: %s " , sstr ( m_filepath ) ) ;
2017-12-04 21:59:28 +01:00
// Create new map from database
ReadJSON ( QJsonDocument : : fromJson ( data ) . object ( ) , online ) ;
return ;
}
if ( QSslSocket : : supportsSsl ( ) = = false )
{
2020-02-01 05:15:50 +01:00
compat_log . error ( " Can not retrieve the online database! Please make sure your system supports SSL. Visit our quickstart guide for more information: https://rpcs3.net/quickstart " ) ;
2019-10-23 01:04:34 +02:00
const QString message = tr ( " Can not retrieve the online database!<br>Please make sure your system supports SSL.<br>Visit our <a href='https://rpcs3.net/quickstart'>quickstart guide</a> for more information. " ) ;
QMessageBox box ( QMessageBox : : Icon : : Warning , tr ( " Warning! " ) , message , QMessageBox : : StandardButton : : Ok , nullptr ) ;
box . setTextFormat ( Qt : : RichText ) ;
box . exec ( ) ;
2017-12-04 21:59:28 +01:00
return ;
}
2020-02-01 05:15:50 +01:00
compat_log . notice ( " SSL supported! Beginning compatibility database download from: %s " , sstr ( m_url ) ) ;
2017-12-04 21:59:28 +01:00
// Send request and wait for response
m_network_access_manager . reset ( new QNetworkAccessManager ( ) ) ;
QNetworkReply * network_reply = m_network_access_manager - > get ( m_network_request ) ;
// Show Progress
2020-01-07 22:44:23 +01:00
m_progress_dialog = new progress_dialog ( tr ( " Downloading Database " ) , tr ( " .Please wait. " ) , tr ( " Abort " ) , 0 , 100 , true ) ;
2017-12-04 21:59:28 +01:00
m_progress_dialog - > show ( ) ;
// Animate progress dialog a bit more
m_progress_timer . reset ( new QTimer ( this ) ) ;
connect ( m_progress_timer . get ( ) , & QTimer : : timeout , [ & ] ( )
{
switch ( + + m_timer_count % 3 )
{
case 0 :
m_timer_count = 0 ;
m_progress_dialog - > setLabelText ( tr ( " .Please wait. " ) ) ;
break ;
case 1 :
m_progress_dialog - > setLabelText ( tr ( " ..Please wait.. " ) ) ;
break ;
default :
m_progress_dialog - > setLabelText ( tr ( " ...Please wait... " ) ) ;
break ;
}
} ) ;
m_progress_timer - > start ( 500 ) ;
// Handle abort
2019-05-17 23:06:18 +02:00
connect ( m_progress_dialog , & QProgressDialog : : canceled , network_reply , & QNetworkReply : : abort ) ;
connect ( m_progress_dialog , & QProgressDialog : : finished , m_progress_dialog , & QProgressDialog : : deleteLater ) ;
2017-12-04 21:59:28 +01:00
// Handle progress
connect ( network_reply , & QNetworkReply : : downloadProgress , [ & ] ( qint64 bytesReceived , qint64 bytesTotal )
{
m_progress_dialog - > setMaximum ( bytesTotal ) ;
m_progress_dialog - > setValue ( bytesReceived ) ;
} ) ;
2019-10-23 01:18:13 +02:00
// Handle network error
2020-02-10 09:55:53 +01:00
connect ( network_reply , QOverload < QNetworkReply : : NetworkError > : : of ( & QNetworkReply : : error ) , [ = , this ] ( QNetworkReply : : NetworkError error )
2017-12-04 21:59:28 +01:00
{
2019-10-23 01:18:13 +02:00
if ( error = = QNetworkReply : : NoError )
{
return ;
}
if ( error ! = QNetworkReply : : OperationCanceledError )
{
// We failed to retrieve a new database, therefore refresh gamelist to old state
const QString error = network_reply - > errorString ( ) ;
Q_EMIT DownloadError ( error ) ;
2020-02-04 13:40:02 +01:00
compat_log . error ( " Network Error - %s " , sstr ( error ) ) ;
2019-10-23 01:18:13 +02:00
}
2017-12-04 21:59:28 +01:00
// Clean up Progress Dialog
if ( m_progress_dialog )
{
m_progress_dialog - > close ( ) ;
}
if ( m_progress_timer )
{
m_progress_timer - > stop ( ) ;
}
2019-10-23 01:18:13 +02:00
network_reply - > deleteLater ( ) ;
} ) ;
// Handle response according to its contents
2020-02-10 09:55:53 +01:00
connect ( network_reply , & QNetworkReply : : finished , [ = , this ] ( )
2019-10-23 01:18:13 +02:00
{
if ( network_reply - > error ( ) ! = QNetworkReply : : NoError )
2018-05-01 03:21:24 +02:00
{
return ;
}
2019-10-23 01:18:13 +02:00
// Clean up Progress Dialog
if ( m_progress_dialog )
2017-12-04 21:59:28 +01:00
{
2019-10-23 01:18:13 +02:00
m_progress_dialog - > close ( ) ;
}
if ( m_progress_timer )
{
m_progress_timer - > stop ( ) ;
2017-12-04 21:59:28 +01:00
}
2020-02-04 13:40:02 +01:00
compat_log . notice ( " Database download finished " ) ;
2017-12-04 21:59:28 +01:00
// Read data from network reply
QByteArray data = network_reply - > readAll ( ) ;
network_reply - > deleteLater ( ) ;
// Create new map from database and write database to file if database was valid
if ( ReadJSON ( QJsonDocument : : fromJson ( data ) . object ( ) , online ) )
{
// We have a new database in map, therefore refresh gamelist to new state
Q_EMIT DownloadFinished ( ) ;
// Write database to file
QFile file ( m_filepath ) ;
if ( file . exists ( ) )
{
2020-02-04 13:40:02 +01:00
compat_log . notice ( " Database file found: %s " , sstr ( m_filepath ) ) ;
2017-12-04 21:59:28 +01:00
}
if ( ! file . open ( QIODevice : : WriteOnly ) )
{
2020-02-04 13:40:02 +01:00
compat_log . error ( " Database Error - Could not write database to file: %s " , sstr ( m_filepath ) ) ;
2017-12-04 21:59:28 +01:00
return ;
}
file . write ( data ) ;
file . close ( ) ;
2020-02-04 13:40:02 +01:00
compat_log . success ( " Write database to file: %s " , sstr ( m_filepath ) ) ;
2017-12-04 21:59:28 +01:00
}
} ) ;
// We want to retrieve a new database, therefore refresh gamelist and indicate that
Q_EMIT DownloadStarted ( ) ;
}
2018-03-16 23:54:56 +01:00
compat_status game_compatibility : : GetCompatibility ( const std : : string & title_id )
2017-12-04 21:59:28 +01:00
{
if ( m_compat_database . empty ( ) )
{
return Status_Data . at ( " NoData " ) ;
}
else if ( m_compat_database . count ( title_id ) > 0 )
{
return m_compat_database [ title_id ] ;
}
return Status_Data . at ( " NoResult " ) ;
}
2018-03-16 23:54:56 +01:00
compat_status game_compatibility : : GetStatusData ( const QString & status )
2017-12-04 21:59:28 +01:00
{
return Status_Data . at ( status ) ;
}