2019-09-11 09:55:43 +02:00
# include " stdafx.h "
# include "update_manager.h"
2020-02-22 20:42:49 +01:00
# include "progress_dialog.h"
2020-03-22 16:15:14 +01:00
# include "localized.h"
2019-09-11 09:55:43 +02:00
# include "rpcs3_version.h"
# include "Utilities/StrUtil.h"
# include "Crypto/sha256.h"
# include "Emu/System.h"
2020-03-21 22:17:15 +01:00
# include <QApplication>
2020-03-22 11:11:42 +01:00
# include <QDateTime>
2019-09-11 09:55:43 +02:00
# include <QMessageBox>
2020-03-13 18:34:08 +01:00
# include <QJsonObject>
# include <QJsonDocument>
# include <QThread>
2019-09-11 09:55:43 +02:00
# if defined(_WIN32)
2020-03-13 18:34:08 +01:00
# define NOMINMAX
2019-09-11 09:55:43 +02:00
# include <windows.h>
# include <CpuArch.h>
# include <7z.h>
# include <7zAlloc.h>
# include <7zBuf.h>
# include <7zCrc.h>
# include <7zFile.h>
# include <7zVersion.h>
# define PATH_MAX MAX_PATH
# else
# include <unistd.h>
# include <sys/stat.h>
# endif
2020-03-22 12:10:57 +01:00
# ifndef CURL_STATICLIB
# define CURL_STATICLIB
# endif
# include <curl/curl.h>
2020-02-22 20:42:49 +01:00
LOG_CHANNEL ( update_log , " UPDATER " ) ;
2020-02-01 05:15:50 +01:00
2020-03-13 18:34:08 +01:00
size_t curl_write_cb ( char * ptr , size_t /*size*/ , size_t nmemb , void * userdata )
{
update_manager * upd_mgr = reinterpret_cast < update_manager * > ( userdata ) ;
return upd_mgr - > update_buffer ( ptr , nmemb ) ;
}
2019-09-11 09:55:43 +02:00
update_manager : : update_manager ( )
{
2020-03-13 18:34:08 +01:00
m_curl = curl_easy_init ( ) ;
# ifdef _WIN32
// This shouldn't be needed on linux
curl_easy_setopt ( m_curl , CURLOPT_CAINFO , " cacert.pem " ) ;
# endif
2020-03-21 22:17:15 +01:00
// We need this signal in order to update the GUI from the main thread
connect ( this , & update_manager : : signal_buffer_update , this , & update_manager : : handle_buffer_update ) ;
}
void update_manager : : handle_buffer_update ( int size )
{
if ( m_progress_dialog & & m_update_dialog )
{
m_progress_dialog - > setValue ( size ) ;
QApplication : : processEvents ( ) ;
}
2020-03-13 18:34:08 +01:00
}
size_t update_manager : : update_buffer ( char * data , size_t size )
{
if ( m_curl_abort )
{
return 0 ;
}
const auto old_size = m_curl_buf . size ( ) ;
const auto new_size = old_size + size ;
m_curl_buf . resize ( static_cast < int > ( new_size ) ) ;
memcpy ( m_curl_buf . data ( ) + old_size , data , size ) ;
2020-03-21 22:17:15 +01:00
Q_EMIT signal_buffer_update ( static_cast < int > ( new_size ) ) ;
2020-03-13 18:34:08 +01:00
return size ;
2019-09-11 09:55:43 +02:00
}
void update_manager : : check_for_updates ( bool automatic , QWidget * parent )
{
2019-11-04 17:08:27 +01:00
# ifdef __linux__
if ( automatic & & ! : : getenv ( " APPIMAGE " ) )
{
// Don't check for updates on startup if RPCS3 is not running from an AppImage.
return ;
}
# endif
2020-03-13 18:34:08 +01:00
m_parent = parent ;
m_curl_abort = false ;
m_update_dialog = false ;
m_curl_buf . clear ( ) ;
2019-09-11 09:55:43 +02:00
2020-01-07 22:44:23 +01:00
m_progress_dialog = new progress_dialog ( tr ( " Checking For Updates " ) , tr ( " Please wait... " ) , tr ( " Abort " ) , 0 , 100 , true , parent ) ;
2019-09-11 09:55:43 +02:00
m_progress_dialog - > setAutoClose ( false ) ;
m_progress_dialog - > setAutoReset ( false ) ;
m_progress_dialog - > show ( ) ;
2020-03-21 22:17:15 +01:00
connect ( m_progress_dialog , & QProgressDialog : : canceled , [ this ] ( ) { m_curl_abort = true ; } ) ;
2019-09-11 09:55:43 +02:00
connect ( m_progress_dialog , & QProgressDialog : : finished , m_progress_dialog , & QProgressDialog : : deleteLater ) ;
2020-03-22 17:53:18 +01:00
const std : : string request_url = " https://update.rpcs3.net/?api=v1&c= " + rpcs3 : : get_commit_and_hash ( ) . second ;
2020-03-13 18:34:08 +01:00
curl_easy_setopt ( m_curl , CURLOPT_URL , request_url . c_str ( ) ) ;
curl_easy_setopt ( m_curl , CURLOPT_WRITEFUNCTION , curl_write_cb ) ;
curl_easy_setopt ( m_curl , CURLOPT_WRITEDATA , this ) ;
2019-09-11 09:55:43 +02:00
2020-03-21 22:17:15 +01:00
auto thread = QThread : : create ( [ this ]
2019-09-11 09:55:43 +02:00
{
2020-03-21 22:17:15 +01:00
const auto curl_result = curl_easy_perform ( m_curl ) ;
m_curl_result = curl_result = = CURLE_OK ;
2019-09-11 09:55:43 +02:00
2020-03-21 22:17:15 +01:00
if ( ! m_curl_result )
2020-03-13 18:34:08 +01:00
{
2020-03-21 22:17:15 +01:00
update_log . error ( " Curl error(query): %s " , curl_easy_strerror ( curl_result ) ) ;
2020-03-13 18:34:08 +01:00
}
2020-03-21 22:17:15 +01:00
} ) ;
connect ( thread , & QThread : : finished , this , [ this , automatic ] ( )
{
const bool result_json = m_curl_result & & handle_json ( automatic ) ;
2019-09-11 09:55:43 +02:00
2020-03-21 22:17:15 +01:00
if ( ! result_json & & ! m_curl_abort )
2020-03-13 18:34:08 +01:00
{
2020-03-21 22:17:15 +01:00
if ( ! automatic )
{
QMessageBox : : warning ( m_parent , tr ( " Auto-updater " ) , tr ( " An error occurred during the auto-updating process. \n Check the log for more information. " ) ) ;
}
if ( m_progress_dialog )
{
m_progress_dialog - > close ( ) ;
m_progress_dialog = nullptr ;
}
2020-03-13 18:34:08 +01:00
}
2020-03-21 22:17:15 +01:00
} ) ;
thread - > setObjectName ( " RPCS3 Update Check " ) ;
thread - > start ( ) ;
2019-09-11 09:55:43 +02:00
}
2020-03-13 18:34:08 +01:00
bool update_manager : : handle_json ( bool automatic )
2019-09-11 09:55:43 +02:00
{
2020-03-13 18:34:08 +01:00
const QJsonObject json_data = QJsonDocument : : fromJson ( m_curl_buf ) . object ( ) ;
2020-03-21 22:17:15 +01:00
const int return_code = json_data [ " return_code " ] . toInt ( - 255 ) ;
2019-09-11 09:55:43 +02:00
bool hash_found = true ;
if ( return_code < 0 )
{
std : : string error_message ;
switch ( return_code )
{
case - 1 : error_message = " Hash not found(Custom/PR build) " ; break ;
case - 2 : error_message = " Server Error - Maintenance Mode " ; break ;
case - 3 : error_message = " Server Error - Illegal Search " ; break ;
case - 255 : error_message = " Server Error - Return code not found " ; break ;
default : error_message = " Server Error - Unknown Error " ; break ;
}
if ( return_code ! = - 1 )
2020-03-02 07:53:30 +01:00
update_log . error ( " Update error: %s return code: %d " , error_message , return_code ) ;
2019-09-11 09:55:43 +02:00
else
2020-03-02 07:53:30 +01:00
update_log . warning ( " Update error: %s return code: %d " , error_message , return_code ) ;
2019-09-11 09:55:43 +02:00
// If a user clicks "Check for Updates" with a custom build ask him if he's sure he wants to update to latest version
if ( ! automatic & & return_code = = - 1 )
{
hash_found = false ;
}
else
{
return false ;
}
}
2020-03-22 15:09:37 +01:00
const auto & current = json_data [ " current_build " ] ;
2019-09-11 09:55:43 +02:00
const auto & latest = json_data [ " latest_build " ] ;
2020-03-22 15:09:37 +01:00
2019-09-11 09:55:43 +02:00
if ( ! latest . isObject ( ) )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " JSON doesn't contain latest_build section " ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
QString os ;
# ifdef _WIN32
os = " windows " ;
# elif defined(__linux__)
os = " linux " ;
# else
2020-03-02 07:53:30 +01:00
update_log . error ( " Your OS isn't currently supported by the auto-updater " ) ;
2019-09-11 09:55:43 +02:00
return false ;
# endif
// Check that every bit of info we need is there
if ( ! latest [ os ] . isObject ( ) | | ! latest [ os ] [ " download " ] . isString ( ) | | ! latest [ os ] [ " size " ] . isDouble ( ) | | ! latest [ os ] [ " checksum " ] . isString ( ) | | ! latest [ " version " ] . isString ( ) | |
! latest [ " datetime " ] . isString ( ) | |
2020-03-22 15:09:37 +01:00
( hash_found & & ( ! current . isObject ( ) | | ! current [ " version " ] . isString ( ) | | ! current [ " datetime " ] . isString ( ) ) ) )
2019-09-11 09:55:43 +02:00
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Some information seems unavailable " ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
if ( hash_found & & return_code = = 0 )
{
2020-03-02 07:53:30 +01:00
update_log . success ( " RPCS3 is up to date! " ) ;
2019-09-11 09:55:43 +02:00
m_progress_dialog - > close ( ) ;
if ( ! automatic )
QMessageBox : : information ( m_parent , tr ( " Auto-updater " ) , tr ( " Your version is already up to date! " ) ) ;
return true ;
}
2020-03-22 11:11:42 +01:00
// Calculate how old the build is
const QString date_fmt = QStringLiteral ( " yyyy-MM-dd hh:mm:ss " ) ;
2019-09-11 09:55:43 +02:00
2020-03-22 15:09:37 +01:00
const QDateTime cur_date = hash_found ? QDateTime : : fromString ( current [ " datetime " ] . toString ( ) , date_fmt ) : QDateTime : : currentDateTimeUtc ( ) ;
2020-03-22 11:11:42 +01:00
const QDateTime lts_date = QDateTime : : fromString ( latest [ " datetime " ] . toString ( ) , date_fmt ) ;
2019-09-11 09:55:43 +02:00
2020-03-22 14:58:00 +01:00
const QString cur_str = cur_date . toString ( date_fmt ) ;
const QString lts_str = lts_date . toString ( date_fmt ) ;
2020-03-22 16:15:14 +01:00
const qint64 diff_msec = cur_date . msecsTo ( lts_date ) ;
2019-09-11 09:55:43 +02:00
2020-03-22 14:58:00 +01:00
update_log . notice ( " Current: %s, latest: %s, difference: %lld ms " , cur_str . toStdString ( ) , lts_str . toStdString ( ) , diff_msec ) ;
2020-03-22 16:15:14 +01:00
Localized localized ;
2019-09-11 09:55:43 +02:00
2020-03-22 11:11:42 +01:00
QString message ;
2019-09-11 09:55:43 +02:00
2020-03-22 11:11:42 +01:00
if ( hash_found )
{
2020-03-22 16:15:14 +01:00
message = tr ( " A new version of RPCS3 is available! \n \n Current version: %0 (%1) \n Latest version: %2 (%3) \n Your version is %4 old. \n \n Do you want to update? " )
2020-03-22 15:09:37 +01:00
. arg ( current [ " version " ] . toString ( ) )
2020-03-22 14:58:00 +01:00
. arg ( cur_str )
2020-03-22 11:11:42 +01:00
. arg ( latest [ " version " ] . toString ( ) )
2020-03-22 14:58:00 +01:00
. arg ( lts_str )
2020-03-22 16:15:14 +01:00
. arg ( localized . GetVerboseTimeByMs ( diff_msec ) ) ;
2020-03-22 11:11:42 +01:00
}
else
{
2020-03-22 16:15:14 +01:00
message = tr ( " You're currently using a custom or PR build. \n \n Latest version: %0 (%1) \n The latest version is %2 old. \n \n Do you want to update to the latest official RPCS3 version? " )
2020-03-22 11:11:42 +01:00
. arg ( latest [ " version " ] . toString ( ) )
2020-03-22 14:58:00 +01:00
. arg ( lts_str )
2020-03-22 16:15:14 +01:00
. arg ( localized . GetVerboseTimeByMs ( std : : abs ( diff_msec ) ) ) ;
2020-03-22 11:11:42 +01:00
}
2019-09-11 09:55:43 +02:00
2020-03-22 11:11:42 +01:00
if ( QMessageBox : : question ( m_progress_dialog , tr ( " Update Available " ) , message , QMessageBox : : Yes | QMessageBox : : No ) = = QMessageBox : : No )
{
m_progress_dialog - > close ( ) ;
return true ;
2019-09-11 09:55:43 +02:00
}
m_expected_hash = latest [ os ] [ " checksum " ] . toString ( ) . toStdString ( ) ;
m_expected_size = latest [ os ] [ " size " ] . toInt ( ) ;
m_progress_dialog - > setWindowTitle ( tr ( " Downloading Update " ) ) ;
// Download RPCS3
2020-03-13 18:34:08 +01:00
m_progress_dialog - > setMaximum ( m_expected_size ) ;
2019-09-11 09:55:43 +02:00
m_progress_dialog - > setValue ( 0 ) ;
2020-03-13 18:34:08 +01:00
m_update_dialog = true ;
2019-09-11 09:55:43 +02:00
2020-03-13 18:34:08 +01:00
const std : : string request_url = latest [ os ] [ " download " ] . toString ( ) . toStdString ( ) ;
curl_easy_setopt ( m_curl , CURLOPT_URL , request_url . c_str ( ) ) ;
curl_easy_setopt ( m_curl , CURLOPT_FOLLOWLOCATION , 1 ) ;
2019-09-11 09:55:43 +02:00
2020-03-13 18:34:08 +01:00
m_curl_buf . clear ( ) ;
2019-09-11 09:55:43 +02:00
2020-03-21 22:17:15 +01:00
auto thread = QThread : : create ( [ this ]
2019-09-11 09:55:43 +02:00
{
2020-03-21 22:17:15 +01:00
const auto curl_result = curl_easy_perform ( m_curl ) ;
m_curl_result = curl_result = = CURLE_OK ;
2020-03-13 18:34:08 +01:00
2020-03-21 22:17:15 +01:00
if ( ! m_curl_result )
2020-03-13 18:34:08 +01:00
{
update_log . error ( " Curl error(download): %s " , curl_easy_strerror ( curl_result ) ) ;
}
2020-03-21 22:17:15 +01:00
} ) ;
connect ( thread , & QThread : : finished , this , [ this , automatic ] ( )
{
const bool result_rpcs3 = m_curl_result & & handle_rpcs3 ( ) ;
2020-03-13 18:34:08 +01:00
if ( ! result_rpcs3 & & ! m_curl_abort )
{
if ( ! automatic )
{
QMessageBox : : warning ( m_parent , tr ( " Auto-updater " ) , tr ( " An error occurred during the auto-updating process. \n Check the log for more information. " ) ) ;
}
if ( m_progress_dialog )
{
m_progress_dialog - > close ( ) ;
m_progress_dialog = nullptr ;
}
}
2020-03-21 22:17:15 +01:00
} ) ;
thread - > setObjectName ( " RPCS3 Updater " ) ;
thread - > start ( ) ;
2019-09-11 09:55:43 +02:00
return true ;
}
2020-03-13 18:34:08 +01:00
bool update_manager : : handle_rpcs3 ( )
2019-09-11 09:55:43 +02:00
{
2020-03-13 18:34:08 +01:00
if ( m_expected_size ! = m_curl_buf . size ( ) + 0u )
2019-09-11 09:55:43 +02:00
{
2020-03-13 18:34:08 +01:00
update_log . error ( " Download size mismatch: %d expected: %d " , m_curl_buf . size ( ) , m_expected_size ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
u8 res_hash [ 32 ] ;
mbedtls_sha256_context ctx ;
mbedtls_sha256_init ( & ctx ) ;
mbedtls_sha256_starts_ret ( & ctx , 0 ) ;
2020-03-13 18:34:08 +01:00
mbedtls_sha256_update_ret ( & ctx , reinterpret_cast < const unsigned char * > ( m_curl_buf . data ( ) ) , m_curl_buf . size ( ) ) ;
2019-09-11 09:55:43 +02:00
mbedtls_sha256_finish_ret ( & ctx , res_hash ) ;
std : : string res_hash_string ( " 0000000000000000000000000000000000000000000000000000000000000000 " ) ;
for ( size_t index = 0 ; index < 32 ; index + + )
{
constexpr auto pal = " 0123456789ABCDEF " ;
res_hash_string [ index * 2 ] = pal [ res_hash [ index ] > > 4 ] ;
res_hash_string [ ( index * 2 ) + 1 ] = pal [ res_hash [ index ] & 15 ] ;
}
if ( m_expected_hash ! = res_hash_string )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Hash mismatch: %s expected: %s " , res_hash_string , m_expected_hash ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
std : : string replace_path ;
2020-03-08 18:40:23 +01:00
# ifdef _WIN32
// Get executable path
wchar_t orig_path [ 32767 ] ;
GetModuleFileNameW ( nullptr , orig_path , sizeof ( orig_path ) / 2 ) ;
# endif
2019-09-11 09:55:43 +02:00
# ifdef __linux__
const char * appimage_path = : : getenv ( " APPIMAGE " ) ;
if ( appimage_path ! = nullptr )
{
replace_path = appimage_path ;
2020-03-02 07:53:30 +01:00
update_log . notice ( " Found AppImage path: %s " , appimage_path ) ;
2019-09-11 09:55:43 +02:00
}
else
{
2020-03-02 07:53:30 +01:00
update_log . warning ( " Failed to find AppImage path " ) ;
2019-09-11 09:55:43 +02:00
char exe_path [ PATH_MAX ] ;
ssize_t len = : : readlink ( " /proc/self/exe " , exe_path , sizeof ( exe_path ) - 1 ) ;
if ( len = = - 1 )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to find executable path " ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
exe_path [ len ] = ' \0 ' ;
2020-03-02 07:53:30 +01:00
update_log . trace ( " Found exec path: %s " , exe_path ) ;
2019-09-11 09:55:43 +02:00
replace_path = exe_path ;
}
m_progress_dialog - > setWindowTitle ( tr ( " Updating RPCS3 " ) ) ;
// Move the appimage/exe and replace with new appimage
2020-03-21 22:17:15 +01:00
const std : : string move_dest = replace_path + " _old " ;
2019-11-01 23:33:56 +01:00
fs : : rename ( replace_path , move_dest , true ) ;
2019-09-11 09:55:43 +02:00
fs : : file new_appimage ( replace_path , fs : : read + fs : : write + fs : : create + fs : : trunc ) ;
if ( ! new_appimage )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to create new AppImage file: %s " , replace_path ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
2020-03-13 18:34:08 +01:00
if ( new_appimage . write ( m_curl_buf . data ( ) , m_curl_buf . size ( ) ) ! = m_curl_buf . size ( ) + 0u )
2019-09-11 09:55:43 +02:00
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to write new AppImage file: %s " , replace_path ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
if ( fchmod ( new_appimage . get_handle ( ) , S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) = = - 1 )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to chmod rwxrxrx %s " , replace_path ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
new_appimage . close ( ) ;
2020-03-02 07:53:30 +01:00
update_log . success ( " Successfully updated %s! " , replace_path ) ;
2019-09-11 09:55:43 +02:00
# elif defined(_WIN32)
char temp_path [ PATH_MAX ] ;
GetTempPathA ( sizeof ( temp_path ) - 1 , temp_path ) ;
temp_path [ PATH_MAX - 1 ] = 0 ;
std : : string tmpfile_path = temp_path ;
tmpfile_path + = " \\ rpcs3_update.7z " ;
fs : : file tmpfile ( tmpfile_path , fs : : read + fs : : write + fs : : create + fs : : trunc ) ;
if ( ! tmpfile )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to create temporary file: %s " , tmpfile_path ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
2020-03-13 18:34:08 +01:00
if ( tmpfile . write ( m_curl_buf . data ( ) , m_curl_buf . size ( ) ) ! = m_curl_buf . size ( ) )
2019-09-11 09:55:43 +02:00
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to write temporary file: %s " , tmpfile_path ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
tmpfile . close ( ) ;
m_progress_dialog - > setWindowTitle ( tr ( " Updating RPCS3 " ) ) ;
// 7z stuff (most of this stuff is from 7z Util sample and has been reworked to be more stl friendly)
ISzAlloc allocImp ;
ISzAlloc allocTempImp ;
CFileInStream archiveStream ;
CLookToRead2 lookStream ;
CSzArEx db ;
SRes res ;
UInt16 temp_u16 [ PATH_MAX ] ;
u8 temp_u8 [ PATH_MAX ] ;
2020-03-02 07:53:30 +01:00
const size_t kInputBufSize = static_cast < size_t > ( 1u < < 18u ) ;
2019-09-11 09:55:43 +02:00
const ISzAlloc g_Alloc = { SzAlloc , SzFree } ;
allocImp = g_Alloc ;
allocTempImp = g_Alloc ;
if ( InFile_Open ( & archiveStream . file , tmpfile_path . c_str ( ) ) )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to open temporary storage file: %s " , tmpfile_path ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
FileInStream_CreateVTable ( & archiveStream ) ;
LookToRead2_CreateVTable ( & lookStream , False ) ;
2020-03-02 07:53:30 +01:00
lookStream . buf = nullptr ;
2019-09-11 09:55:43 +02:00
res = SZ_OK ;
{
lookStream . buf = ( Byte * ) ISzAlloc_Alloc ( & allocImp , kInputBufSize ) ;
if ( ! lookStream . buf )
res = SZ_ERROR_MEM ;
else
{
lookStream . bufSize = kInputBufSize ;
lookStream . realStream = & archiveStream . vt ;
LookToRead2_Init ( & lookStream ) ;
}
}
CrcGenerateTable ( ) ;
SzArEx_Init ( & db ) ;
2020-03-21 22:17:15 +01:00
auto error_free7z = [ & ] ( )
{
2019-09-11 09:55:43 +02:00
SzArEx_Free ( & db , & allocImp ) ;
ISzAlloc_Free ( & allocImp , lookStream . buf ) ;
File_Close ( & archiveStream . file ) ;
switch ( res )
{
case SZ_OK : break ;
2020-03-02 07:53:30 +01:00
case SZ_ERROR_UNSUPPORTED : update_log . error ( " 7z decoder doesn't support this archive " ) ; break ;
case SZ_ERROR_MEM : update_log . error ( " 7z decoder failed to allocate memory " ) ; break ;
case SZ_ERROR_CRC : update_log . error ( " 7z decoder CRC error " ) ; break ;
default : update_log . error ( " 7z decoder error: %d " , static_cast < u64 > ( res ) ) ; break ;
2019-09-11 09:55:43 +02:00
}
} ;
if ( res ! = SZ_OK )
{
error_free7z ( ) ;
return false ;
}
res = SzArEx_Open ( & db , & lookStream . vt , & allocImp , & allocTempImp ) ;
if ( res ! = SZ_OK )
{
error_free7z ( ) ;
return false ;
}
UInt32 blockIndex = 0xFFFFFFFF ;
2020-03-02 07:53:30 +01:00
Byte * outBuffer = nullptr ;
2019-09-11 09:55:43 +02:00
size_t outBufferSize = 0 ;
// Creates temp folder for moving active files
2020-03-22 17:53:18 +01:00
const std : : string tmp_folder = Emulator : : GetEmuDir ( ) + " rpcs3_old/ " ;
2019-09-11 09:55:43 +02:00
fs : : create_dir ( tmp_folder ) ;
for ( UInt32 i = 0 ; i < db . NumFiles ; i + + )
{
size_t offset = 0 ;
size_t outSizeProcessed = 0 ;
size_t len ;
unsigned isDir = SzArEx_IsDir ( & db , i ) ;
2020-03-02 07:53:30 +01:00
len = SzArEx_GetFileNameUtf16 ( & db , i , nullptr ) ;
2019-09-11 09:55:43 +02:00
if ( len > = PATH_MAX )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " 7z decoder error: filename longer or equal to PATH_MAX " ) ;
2019-09-11 09:55:43 +02:00
error_free7z ( ) ;
return false ;
}
SzArEx_GetFileNameUtf16 ( & db , i , temp_u16 ) ;
memset ( temp_u8 , 0 , sizeof ( temp_u8 ) ) ;
// Simplistic conversion to UTF-8
for ( size_t index = 0 ; index < len ; index + + )
{
if ( temp_u16 [ index ] > 0xFF )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " 7z decoder error: Failed to convert UTF-16 to UTF-8 " ) ;
2019-09-11 09:55:43 +02:00
error_free7z ( ) ;
return false ;
}
2019-12-04 21:56:19 +01:00
temp_u8 [ index ] = static_cast < u8 > ( temp_u16 [ index ] ) ;
2019-09-11 09:55:43 +02:00
}
temp_u8 [ len ] = 0 ;
std : : string name ( ( char * ) temp_u8 ) ;
name = Emulator : : GetEmuDir ( ) + name ;
if ( ! isDir )
{
res = SzArEx_Extract ( & db , & lookStream . vt , i , & blockIndex , & outBuffer , & outBufferSize , & offset , & outSizeProcessed , & allocImp , & allocTempImp ) ;
if ( res ! = SZ_OK )
break ;
}
2020-03-21 22:17:15 +01:00
if ( const size_t pos = name . find_last_of ( ' / ' ) ; pos ! = umax )
2019-09-11 09:55:43 +02:00
{
2020-03-02 07:53:30 +01:00
update_log . trace ( " Creating path: %s " , name . substr ( 0 , pos ) ) ;
2019-09-11 09:55:43 +02:00
fs : : create_path ( name . substr ( 0 , pos ) ) ;
}
if ( isDir )
{
2020-03-02 07:53:30 +01:00
update_log . trace ( " Creating dir: %s " , name ) ;
2019-09-11 09:55:43 +02:00
fs : : create_dir ( name ) ;
continue ;
}
fs : : file outfile ( name , fs : : read + fs : : write + fs : : create + fs : : trunc ) ;
if ( ! outfile )
{
// File failed to open, probably because in use, rename existing file and try again
const auto pos = name . find_last_of ( ' / ' ) ;
std : : string filename ;
2020-03-05 12:05:23 +01:00
if ( pos = = umax )
2019-09-11 09:55:43 +02:00
filename = name ;
else
filename = name . substr ( pos + 1 ) ;
// Moving to temp is not an option on windows as it will fail if the disk is different
// So we create a folder in config dir and move stuff there
const std : : string rename_target = tmp_folder + filename ;
2020-03-02 07:53:30 +01:00
update_log . trace ( " Renaming %s to %s " , name , rename_target ) ;
2019-09-11 09:55:43 +02:00
if ( ! fs : : rename ( name , rename_target , true ) )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Failed to rename %s to %s " , name , rename_target ) ;
2019-09-11 09:55:43 +02:00
res = SZ_ERROR_FAIL ;
break ;
}
outfile . open ( name , fs : : read + fs : : write + fs : : create + fs : : trunc ) ;
if ( ! outfile )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Can not open output file %s " , name ) ;
2019-09-11 09:55:43 +02:00
res = SZ_ERROR_FAIL ;
break ;
}
}
if ( outfile . write ( outBuffer + offset , outSizeProcessed ) ! = outSizeProcessed )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Can not write output file: %s " , name ) ;
2019-09-11 09:55:43 +02:00
res = SZ_ERROR_FAIL ;
break ;
}
outfile . close ( ) ;
}
error_free7z ( ) ;
if ( res )
return false ;
# endif
QMessageBox : : information ( m_parent , tr ( " Auto-updater " ) , tr ( " Update successful! " ) ) ;
2019-10-25 12:32:21 +02:00
# ifdef _WIN32
2020-03-21 22:17:15 +01:00
const int ret = _wexecl ( orig_path , orig_path , nullptr ) ;
2019-10-25 12:32:21 +02:00
# else
2020-03-21 22:17:15 +01:00
const int ret = execl ( replace_path . c_str ( ) , replace_path . c_str ( ) , nullptr ) ;
2019-10-25 12:32:21 +02:00
# endif
2019-09-11 09:55:43 +02:00
if ( ret = = - 1 )
{
2020-03-02 07:53:30 +01:00
update_log . error ( " Relaunching failed with result: %d(%s) " , ret , strerror ( errno ) ) ;
2019-09-11 09:55:43 +02:00
return false ;
}
return true ;
}