mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-25 00:49:47 +00:00
283 lines
8.9 KiB
C++
283 lines
8.9 KiB
C++
#include <QDir>
|
|
#include <QApplication>
|
|
#include <QProgressDialog>
|
|
|
|
#include "../ui_qt.h"
|
|
|
|
extern "C" {
|
|
#include <string/stdstring.h>
|
|
#include <streams/file_stream.h>
|
|
#include <file/archive_file.h>
|
|
#include "../../../tasks/tasks_internal.h"
|
|
#include "../../../verbosity.h"
|
|
#include "../../../config.def.h"
|
|
}
|
|
|
|
#define USER_AGENT "RetroArch-WIMP/1.0"
|
|
#define PARTIAL_EXTENSION ".partial"
|
|
#define TEMP_EXTENSION ".update_tmp"
|
|
#define RETROARCH_NIGHTLY_UPDATE_PATH "../RetroArch_update.zip"
|
|
|
|
static void extractCB(void *task_data, void *user_data, const char *err)
|
|
{
|
|
decompress_task_data_t *dec = (decompress_task_data_t*)task_data;
|
|
MainWindow *mainwindow = (MainWindow*)user_data;
|
|
|
|
if (err)
|
|
RARCH_ERR("%s", err);
|
|
|
|
if (dec)
|
|
{
|
|
if (filestream_exists(dec->source_file))
|
|
filestream_delete(dec->source_file);
|
|
|
|
free(dec->source_file);
|
|
free(dec);
|
|
}
|
|
|
|
mainwindow->onUpdateRetroArchFinished(string_is_empty(err));
|
|
}
|
|
|
|
void MainWindow::removeUpdateTempFiles()
|
|
{
|
|
/* a QDir with no path means the current working directory */
|
|
QDir dir;
|
|
QStringList dirList = dir.entryList(QStringList(), QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, QDir::Name);
|
|
int i;
|
|
|
|
for (i = 0; i < dirList.count(); i++)
|
|
{
|
|
QString path(dir.path() + "/" + dirList.at(i));
|
|
QFile file(path);
|
|
|
|
if (path.endsWith(TEMP_EXTENSION))
|
|
{
|
|
QByteArray pathArray = path.toUtf8();
|
|
const char *pathData = pathArray.constData();
|
|
|
|
if (file.remove())
|
|
RARCH_LOG("[Qt]: removed temporary update file %s\n", pathData);
|
|
else
|
|
RARCH_LOG("[Qt]: could not remove temporary update file %s\n", pathData);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::onUpdateNetworkError(QNetworkReply::NetworkError code)
|
|
{
|
|
QNetworkReply *reply = m_updateReply.data();
|
|
QByteArray errorStringArray;
|
|
const char *errorStringData = NULL;
|
|
|
|
m_updateProgressDialog->cancel();
|
|
|
|
if (!reply)
|
|
return;
|
|
|
|
errorStringArray = reply->errorString().toUtf8();
|
|
errorStringData = errorStringArray.constData();
|
|
|
|
RARCH_ERR("[Qt]: Network error code %d received: %s\n", code, errorStringData);
|
|
|
|
/* Deleting the reply here seems to cause a strange heap-use-after-free crash. */
|
|
/*
|
|
reply->disconnect();
|
|
reply->abort();
|
|
reply->deleteLater();
|
|
*/
|
|
}
|
|
|
|
void MainWindow::onUpdateNetworkSslErrors(const QList<QSslError> &errors)
|
|
{
|
|
QNetworkReply *reply = m_updateReply.data();
|
|
int i;
|
|
|
|
if (!reply)
|
|
return;
|
|
|
|
for (i = 0; i < errors.count(); i++)
|
|
{
|
|
const QSslError &error = errors.at(i);
|
|
QString string = QString("Ignoring SSL error code ") + QString::number(error.error()) + ": " + error.errorString();
|
|
QByteArray stringArray = string.toUtf8();
|
|
const char *stringData = stringArray.constData();
|
|
RARCH_ERR("[Qt]: %s\n", stringData);
|
|
}
|
|
|
|
/* ignore all SSL errors for now, like self-signed, expired etc. */
|
|
reply->ignoreSslErrors();
|
|
}
|
|
|
|
void MainWindow::onUpdateDownloadCanceled()
|
|
{
|
|
m_updateProgressDialog->cancel();
|
|
}
|
|
|
|
void MainWindow::onRetroArchUpdateDownloadFinished()
|
|
{
|
|
QNetworkReply *reply = m_updateReply.data();
|
|
QNetworkReply::NetworkError error;
|
|
int code;
|
|
|
|
m_updateProgressDialog->cancel();
|
|
|
|
/* At least on Linux, the progress dialog will refuse to hide itself and will stay on screen in a corrupted way if we happen to show an error message in this function. processEvents() will sometimes fix it, other times not... seems random. */
|
|
qApp->processEvents();
|
|
|
|
if (!reply)
|
|
return;
|
|
|
|
error = reply->error();
|
|
code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
if (m_updateFile.isOpen())
|
|
m_updateFile.close();
|
|
|
|
if (code != 200)
|
|
{
|
|
emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": HTTP Code " + QString::number(code));
|
|
RARCH_ERR("[Qt]: RetroArch update failed with HTTP status code: %d\n", code);
|
|
reply->disconnect();
|
|
reply->abort();
|
|
reply->deleteLater();
|
|
return;
|
|
}
|
|
|
|
if (error == QNetworkReply::NoError)
|
|
{
|
|
int index = m_updateFile.fileName().lastIndexOf(PARTIAL_EXTENSION);
|
|
QString newFileName = m_updateFile.fileName().left(index);
|
|
QFile newFile(newFileName);
|
|
|
|
/* rename() requires the old file to be deleted first if it exists */
|
|
if (newFile.exists() && !newFile.remove())
|
|
RARCH_ERR("[Qt]: RetroArch update finished, but old file could not be deleted.\n");
|
|
else
|
|
{
|
|
if (m_updateFile.rename(newFileName))
|
|
{
|
|
RARCH_LOG("[Qt]: RetroArch update finished downloading successfully.\n");
|
|
emit extractArchiveDeferred(newFileName, ".", TEMP_EXTENSION, extractCB);
|
|
}
|
|
else
|
|
{
|
|
RARCH_ERR("[Qt]: RetroArch update finished, but temp file could not be renamed.\n");
|
|
emit showErrorMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QByteArray errorArray = reply->errorString().toUtf8();
|
|
const char *errorData = errorArray.constData();
|
|
|
|
m_updateFile.remove();
|
|
|
|
RARCH_ERR("[Qt]: RetroArch update ended prematurely: %s\n", errorData);
|
|
emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": Code " + QString::number(code) + ": " + errorData);
|
|
}
|
|
|
|
reply->disconnect();
|
|
reply->close();
|
|
reply->deleteLater();
|
|
}
|
|
|
|
void MainWindow::onUpdateRetroArchFinished(bool success)
|
|
{
|
|
m_updateProgressDialog->cancel();
|
|
|
|
if (!success)
|
|
{
|
|
RARCH_ERR("[Qt]: RetroArch update failed.\n");
|
|
emit showErrorMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_FAILED));
|
|
return;
|
|
}
|
|
|
|
RARCH_LOG("[Qt]: RetroArch update finished successfully.\n");
|
|
|
|
emit showInfoMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_FINISHED));
|
|
}
|
|
|
|
void MainWindow::onUpdateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
|
{
|
|
QNetworkReply *reply = m_updateReply.data();
|
|
int progress = (bytesReceived / (float)bytesTotal) * 100.0f;
|
|
|
|
if (!reply)
|
|
return;
|
|
|
|
m_updateProgressDialog->setValue(progress);
|
|
}
|
|
|
|
void MainWindow::onUpdateDownloadReadyRead()
|
|
{
|
|
QNetworkReply *reply = m_updateReply.data();
|
|
|
|
if (!reply)
|
|
return;
|
|
|
|
m_updateFile.write(reply->readAll());
|
|
}
|
|
|
|
void MainWindow::updateRetroArchNightly()
|
|
{
|
|
QUrl url(QUrl(buildbot_server_url).resolved(QUrl(RETROARCH_NIGHTLY_UPDATE_PATH)));
|
|
QNetworkRequest request(url);
|
|
QNetworkReply *reply = NULL;
|
|
QByteArray urlArray = url.toString().toUtf8();
|
|
const char *urlData = urlArray.constData();
|
|
|
|
if (m_updateFile.isOpen())
|
|
{
|
|
RARCH_ERR("[Qt]: File is already open.\n");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
QString fileName = QFileInfo(url.toString()).fileName() + PARTIAL_EXTENSION;
|
|
QByteArray fileNameArray = fileName.toUtf8();
|
|
const char *fileNameData = fileNameArray.constData();
|
|
|
|
m_updateFile.setFileName(fileName);
|
|
|
|
if (!m_updateFile.open(QIODevice::WriteOnly))
|
|
{
|
|
showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
|
|
RARCH_ERR("[Qt]: Could not open file for writing: %s\n", fileNameData);
|
|
return;
|
|
}
|
|
}
|
|
|
|
RARCH_LOG("[Qt]: Starting update of RetroArch...\n");
|
|
RARCH_LOG("[Qt]: Downloading URL %s\n", urlData);
|
|
|
|
request.setHeader(QNetworkRequest::UserAgentHeader, USER_AGENT);
|
|
|
|
m_updateProgressDialog->setWindowModality(Qt::NonModal);
|
|
m_updateProgressDialog->setMinimumDuration(0);
|
|
m_updateProgressDialog->setRange(0, 100);
|
|
m_updateProgressDialog->setAutoClose(true);
|
|
m_updateProgressDialog->setAutoReset(true);
|
|
m_updateProgressDialog->setValue(0);
|
|
m_updateProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_DOWNLOADING)) + "...");
|
|
m_updateProgressDialog->setCancelButtonText(tr("Cancel"));
|
|
m_updateProgressDialog->show();
|
|
|
|
m_updateReply = m_networkManager->get(request);
|
|
|
|
reply = m_updateReply.data();
|
|
|
|
/* make sure any previous connection is removed first */
|
|
disconnect(m_updateProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));
|
|
disconnect(m_updateProgressDialog, SIGNAL(canceled()), m_updateProgressDialog, SLOT(cancel()));
|
|
connect(m_updateProgressDialog, SIGNAL(canceled()), reply, SLOT(abort()));
|
|
connect(m_updateProgressDialog, SIGNAL(canceled()), m_updateProgressDialog, SLOT(cancel()));
|
|
|
|
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onUpdateNetworkError(QNetworkReply::NetworkError)));
|
|
connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(onUpdateNetworkSslErrors(const QList<QSslError>&)));
|
|
connect(reply, SIGNAL(finished()), this, SLOT(onRetroArchUpdateDownloadFinished()));
|
|
connect(reply, SIGNAL(readyRead()), this, SLOT(onUpdateDownloadReadyRead()));
|
|
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onUpdateDownloadProgress(qint64, qint64)));
|
|
|
|
}
|